您当前的位置: 首页 >  ar

ZhangJiQun&MXP

暂无认证

  • 1浏览

    0关注

    1187博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

startServic bindService AIDL和Messenger区别 IntentService AIDL

ZhangJiQun&MXP 发布时间:2019-04-30 00:32:34 ,浏览量:1

目录

 

startServic

bindService

AIDL和Messenger区别

IntentService

AIDL

startServic

Service的创建 1) 创建一个类继承Service,当然子类也可以例如IntentService.重写方法:

onCreate():  在每个service的生命周期中这个方法会且仅会调用一次,并且它的调用在onStartCommand()以及onBind()之前,我们可以在这个方法中进行一些一次性的初始化工作。

onStartCommand():  其他组件通过startService()启动service,就会调用这个方法,service的主要操作就在这里.

onBind():  其他组件通过bindService()进行绑定Service,就会调用这个方法.这个方法返回一个IBinder,这意味重写的时候必须返回一个IBinder对象,这个IBinder是用来支撑其他组件和Service通信.假如不希望被绑定,返回一个Null就可以了.

onDestory():  这是service一生中调用的最后一个方法,当这个方法被调用之后,service就会被销毁。所以我们应当在这个方法里面进行一些资源的清理,比如注册的一些监听器什么的。

2)注册

一般我们用名字在Mainfest文件进行声明就好了.  属性介绍:

android:enabled : 如果为true,则这个service可以被系统实例化,如果为false,则不行。默认为true

android:exported : 如果为true,则其他应用的组件也可以调用这个service并且可以与它进行互动,如果为false,则只有与service同一个应用或者相同user ID的应用可以开启或绑定此service。它的默认值取决于service是否有intent filters。如果一个filter都没有,就意味着只有指定了service的准确的类名才能调用,也就是说这个service只能应用内部使用——其他的应用不知道它的类名。这种情况下exported的默认值就为false。反之,只要有了一个filter,就意味着service是考虑到外界使用的情况的,这时exported的默认值就为true

android:icon : 一个象征着这个service的icon

android:isolatedProcess : 如果设置为true,这个service将运行在一个从系统中其他部分分离出来的特殊进程中,我们只能通过Service API来与它进行交流。默认为false。

android:label : 显示给用户的这个service的名字。如果不设置,将会默认使用的label属性。

android:name : 这个service的路径名,例如“com.lypeer.demo.MyService”。这个属性是唯一一个必须填的属性。

android:permission : 其他组件必须具有所填的权限才能启动这个service。

android:process : service运行的进程的name。默认启动的service是运行在主进程中的。

启动Service startServic

bindService

首先我们来说明一下,就是说这个bindService是一种比startService复杂的方式,不过这个BindService的交互比startService更加复杂.

客户端配置 调用bindService()方法.

public boolean bindService(Intent service, ServiceConnection conn, int flags) {     return mBase.bindService(service, conn, flags); } 参数分析:

参数一:intent,指定启动哪一个service以及传递一些数据过去

参数二:ServiceConnection 通信的关键类  重写两个回调方法:  onServiceConnected():  目的获取返回的IBinder接口,类似一个代理类的作用,然后通过这个代理类去获取Service,然后调用其内部的方法.

onServiceDisconnected()  Android系统会在与服务的连接意外中断时(例如当服务崩溃或被终止时)调用该方法。当客户端取消绑定时,系统“绝对不会”调用该方法。

参数三:int值,它是一个指示绑定选项的标志,通常应该是 BIND_AUTO_CREATE,以便创建尚未激活的服务。 其他可能的值为 BIND_DEBUG_UNBIND 和 BIND_NOT_FOREGROUND,或 0(表示无)。

服务端配置 根据上面绑定的Service需求,需要重写OnBind(),返回一个IBinder接口对象,用于交互.

获取IBinder接口 IBinder接口的,一个在整个Android系统中都非常重要的东西,是为高性能而设计的轻量级远程调用机制的核心部分.它不仅仅是内部调用,也可以远程调用.  我们只要记住一个重点:Binder类对象,客户端可以通过这个类似代理类的方式调用服务端的共有方法.

1)继承Binder类(同一个进程间的)  1. service类中创建一个Binder实例(重点在于公共方法)  1) 包含客户端可调用的公共方法  2) 返回当前Service实例,其中有公共方法  3) 由当前service承载的其他类的实例,其中包含客户端可调用的公共方法  方式一:

OnBind()方法返回这个Binder实例. 客户端通过onServiceDisconnected(),接受这个对象,使用这个方法.

2)使用Messenger  Messenger核心是Message以及Handler进行线程间通信.  步骤:

服务端实现一个Handler,接受客户端的调用的回调. 服务端创建Messenger对象通过Messenger得到IBinder对象,返回给客户端. 客户端使用IBinder将Messenger实例化,然后将Message对象发给服务. 服务端Handler接受Message对象,然后实现对应方法. 这种是利用了Handler和Message的机制.

    //服务端     public class MessengerServiceDemo extends Service {

        static final int MSG_SAY_HELLO = 1;

        class ServiceHandler extends Handler {             @Override             public void handleMessage(Message msg) {                 switch (msg.what) {                     case MSG_SAY_HELLO:                         //当收到客户端的message时,显示hello                         Toast.makeText(getApplicationContext(), "hello!", Toast.LENGTH_SHORT).show();                         break;                     default:                         super.handleMessage(msg);                 }             }         }

    final Messenger mMessenger = new Messenger(new ServiceHandler());

    @Nullable     @Override     public IBinder onBind(Intent intent) {         Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show();         //返回给客户端一个IBinder实例         return mMessenger.getBinder();     } } 这里最主要的方法是我们需要在OnBind上面下功夫,要知道Messenger是可以关联Handler,并且可以返回IBinder的.

当然,这里因为是跨进程通讯的,所以要让别的应用知道怎么找到我们Service.就是在注册文件上做文章.

                                                    客户端:

  public class ActivityMessenger extends Activity {

        static final int MSG_SAY_HELLO = 1;

        Messenger mService = null;         boolean mBound;

        private ServiceConnection mConnection = new ServiceConnection() {             public void onServiceConnected(ComponentName className, IBinder service) {                 //接收onBind()传回来的IBinder,并用它构造Messenger                 mService = new Messenger(service);                 mBound = true;             }

            public void onServiceDisconnected(ComponentName className) {                 mService = null;                 mBound = false;             }         };

        //调用此方法时会发送信息给服务端         public void sayHello(View v) {             if (!mBound) return;             //发送一条信息给服务端             Message msg = Message.obtain(null, MSG_SAY_HELLO, 0, 0);             try {                 mService.send(msg);             } catch (RemoteException e) {                 e.printStackTrace();             }         }

        @Override         protected void onCreate(Bundle savedInstanceState) {             super.onCreate(savedInstanceState);             setContentView(R.layout.activity_main);         }

        @Override         protected void onStart() {             super.onStart();             //绑定服务端的服务,此处的action是service在Manifests文件里面声明的             Intent intent = new Intent();             intent.setAction("com.lypeer.messenger");             //不要忘记了包名,不写会报错             intent.setPackage("com.lypeer.ipcserver");             bindService(intent, mConnection, Context.BIND_AUTO_CREATE);         }

        @Override         protected void onStop() {             super.onStop();             // Unbind from the service             if (mBound) {                 unbindService(mConnection);                 mBound = false;             }         }     } 细节:  1) 首先我们需要注意在OnCreate()上面,对Intent做文章,要设置包名,毕竟是跨进程.  2) onServiceConnected()方法获取服务端返回的IBinder,构造Messenger,然后通过Messenger发送Message进行交互.  3)Messenger是一个信使.

AIDL和Messenger区别

这么说,Messenger的内部实现还是AIDL.  Messnger好处:它会把所有的请求排入队列,因此你几乎可以不用担心多线程可能会带来的问题。但是只能实现串行的信息交互.  AIDL:可以令项目中存在大量的并发交互.

IntentService

默认情况下service将工作于应用的主线程,而这将会降低所有正在运行的Activity的性能。而IntentService就不同了. 不需要担心多线程问题,会自动请求停止服务,但是不绑定,会有一个工作队列. 特色:

 创建默认的工作线程,用于在应用的主线程外执行传递给 onStartCommand() 的所有 Intent

 创建工作队列,用于将一个 Intent 逐一传递给 onHandleIntent() 实现,这样的话就永远不必担心多线程问题了

 在处理完所有启动请求后停止服务,从此再也不用担心我忘记调用 stop() 了

 提供 onBind() 的默认实现(返回 null)

 提供 onStartCommand() 的默认实现,可将 Intent 依次发送到工作队列和 onHandleIntent() 实现

 因此我们只需要实现onHandleIntent()方法来完成具体的功能逻辑就可以了。 例子:

    public class IntentServiceDemo extends IntentService {

        public IntentServiceDemo(String name) {             super(name);             //构造方法         }

        @Override         protected void onHandleIntent(Intent intent) {             //在这里根据intent进行操作         }     }

接受Intent,会根据Intent进行操作,但是主要要是重写其他方法,就不要删除超类实现,而且不能处理多个请求.  

AIDL

这个语言的目的是为了实现进程间通信. 在一个进程访问另外一个进程的数据,还有调用特定的方法.

特点:

文件类型: .aidl文件

数据类型:  AIDL默认支持一些数据类型.非默认数据使用前必须导包.使用对象也需要导包.   默认:   Java中的八种基本数据类型,包括 byte,short,int,long,float,double,boolean,char。   String 类型。   CharSequence类型。   List类型:List中的所有元素必须是AIDL支持的类型之一,或者是一个其他AIDL生成的接口,或者是定义的parcelable(下文关于这个会有详解)。List可以使用泛型。   Map类型:Map中的所有元素必须是AIDL支持的类型之一,或者是一个其他AIDL生成的接口,或者是定义的parcelable。Map是不支持泛型的。

定向Tag:除非是基本类型和String ,CharSequence.这些是默认的in in:客户端流向服务端 out:服务端流向客户端 inout:双向流动 大约思路: 客户端传递对象信息给客户端,服务端改动,客户端就会改动.

AIDL类型: 1) 定义parcelable对象,以供其他AIDL文件使用AIDL中非默认支持的数据类型的。 2) 定义方法接口,以供系统使用来完成跨进程通信的.定义接口,里面都是方法.

注意AIDL我们在乎的是定义并非实现. AIDL的实现: 1) 实现Parcelable接口,目的是为了我们传送的数据能够在内存之间流通.过程可以说是序列化和反序列化.  客户端–>序列化对象–>服务端—>反序列化对象

如何快速生成一个可序列化类?目的是为了我们的操作对象能够传.  注意:假如是默认就不需要这样了.

编译器生成:  1. Bean类(成员变量,set,get);  2. 实现Parcelable接口,主动解决错误.(alt+enter)  3. 注意,因为默认是in定向tag,所以只有写出的这么一个方法,假如需要其他就添加.

  @Override     public void writeToParcel(Parcel dest, int flags) {         dest.writeString(name);         dest.writeInt(price);     }

    /**      * 参数是一个Parcel,用它来存储与传输数据      * @param dest      */     public void readFromParcel(Parcel dest) {         //注意,此处的读值顺序应当是和writeToParcel()方法中一致的         name = dest.readString();         price = dest.readInt();     } 2)书写AIDL  Book.aild文件引入后,就能让其他类使用这么一个对象.

AS:  AIDL:直接生成,写好接口.  但是AS的文件路径需要我们处理一下,因为AS上面有分成Aidl包.这时候aidl文件就不能跟自己的.java文件对应了.(因为我们要求文件的报名要一样.)  因为我们的工具是自动到java下找java文件,我们要将java文件放到aidl包的的方式.

修改 build.gradle 文件:在 android{} 中间加上下面的内容:

  sourceSets {      main {          java.srcDirs = ['src/main/java', 'src/main/aidl']      }     } 但是目录很难看.

3)移植文件  我们必须保证客户端服务端都有对应的.aidl和.java文件.  直接复制aidl包就可以了.

4)编写服务端代码  文件移植之后,假如我们clean,编译器就会根据AIDL文件生成.java文件.在服务端实现具体的方法,然后客户端调用方法接口.

AIDL文件的结构:  1. 初始化  2. 重写Stub类中的方法,这其中有AIDL的方法接口的具体逻辑,可以说是引入AIDL文件生成的对象.  3. OnBind(),返回Stub代理类

5)编写客户端代码.  1. 初始化AIDL生成的java类对象  2. 通过Intent进行bindService绑定.  3. Conn就利用Stub作为对象类.调用其中的方法.

AIDL本质工作: 1) AIDL目的是为了让编译器帮我们生成一个.java文件  2) 生成的.java文件会有一个类似Stub的代理类,服务端重写了之前AIDL中定义的方法,就是这个Stub中的方法,并且通过OnBind()返回.  3) 客户端捕获这个对象然后调用.

我们的分析从应用开始:  1. 使用的是.java文件,但是它是一个接口类.实际我们调用的是它的一个实现类.获取的方法是通过BookManager.Stub.asInterface(BinderProxy 对象)方法获取.  2. BinderProxy是实现了BookManager接口的一个类,也就是我们上文使用的这么一个类.它是经过搜查得到的.也是我们最终需要进行交互的一个类.

而Proxy的方法工作流程:  1,生成 _data 和 _reply 数据流,并向 _data 中存入客户端的数据。  2,通过 transact() 方法将它们传递给服务端,并请求服务端调用指定方法。  3,接收 _reply 数据流,并从中取出服务端传回来的数据。

客户端–>存入_data–>transact()–>传给服务器–>reply()–>服务器返回的数据.

服务端分析 .java文件中有个方法onTransact(),负责接收传递过来的信息.  工作流程:  1,获取客户端传过来的数据,根据方法 ID 执行相应操作。  2,将传过来的数据取出来,调用本地写好的对应方法。  3,将需要回传的数据写入 reply 流,传回客户端。

总结:  客户端代理服务端  其中我们的过程就是一个通过代理进行交互的过程.  

关注
打赏
1665659684
查看更多评论
立即登录/注册

微信扫码登录

0.0489s