详细阅读后增加了几个知识点。


1、AIDI接口文件中,自定义实体对象作为参数时,需要在其前面加上in、out、inout标识。这几个标识的意思是:


被“in”标记的参数,就是接收实际数据的参数,这个跟我们普通参数传递一样的含义。在AIDL中,“out” 指定了一个仅用于输出的参数,换而言之,这个参数不关心调用方传递了什么数据过来,但是这个参数的值可以在方法被调用后填充(无论调用方传递了什么值过来,在方法执行的时候,这个参数的初始值总是空的),这就是“out”的含义,仅用于输出。而“inout”显然就是“in”和“out”的合体了,输入和输出的参数。区分“in”、“out”有什么用?这是非常重要的,因为每个参数的内容必须编组(序列化,传输,接收和反序列化)。in/out标签允许Binder跳过编组步骤以获得更好的性能。



2、客户端在bindService的时候可以拿到服务端的IBinder对象,继而可以直接使用这个IBinder对象调用服务端的方法。而服务端想要主动通知客户端的时候怎么办呢?增加一个回调接口,这个接口在客户端来实现,通过调用服务端的注册接口注册这个回调。那么服务端是用RemoteCallbackList<>来管理客户端的回调接口,替代了普通的ArrayList。



3、服务端进程发生了crash,客户端这个时候该怎么办?当然是想办法重新bind服务端了。


客户端使用DeathRecipient对象,可以知道服务端binder何时死完。有两个重要的方法:


  1. linkToDeath -> 设置死亡代理 DeathRecipient 对象;
  2. unlinkToDeath -> Binder死亡的情况下,解除该代理。
  3. 此外,Binder中的isBinderAlive也可以判断Binder是否死亡

DeathRecipient是IBinder接口的内部接口,只有一个binderDied方法:


public interface DeathRecipient {
        public void binderDied();
    }


我们在客户端拿到服务端的IBinder接口的时候进行注册,使用IBinder的linkToDeath方法,


在客户端LocalActivity中


connection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                Log.i(tag, "onServiceConnected");
                mMessageSender = MessageSender.Stub.asInterface(service);
                try {
                    mMessageSender.asBinder().linkToDeath(mDeathRecipient,0);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void onServiceDisconnected(ComponentName name) {
                Log.i(tag, "onServiceDisconnected");
            }
        };


private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
        @Override
        public void binderDied() {
            Log.i(tag, "binderDied");
        }
    };


这样一旦服务端进程销毁了,就会回调binderDied方法,异常终止服务端进程,客户端打印了如下:


07-20 16:26:02.367 18169-18183/com.txt.mydemo I/LocalActivity: binderDied
07-20 16:26:02.367 18169-18169/com.txt.mydemo I/LocalActivity: onServiceDisconnected


以上是客户端知道服务端异常终止,那么服务端可不可能也监听客户端是否终止呢?


答案是可以的,我们只需要在客户端的activity中创建一个Binder实例,在把这个binder实例传递给服务端,服务端拿到这个实例后,进行注册服务端声明的DeathRecipient的对象,从而就能在回调中监听客户端的状态了。要从客户端传递对象到服务端,必然要通过aidl接口来实现,为了方便,我稍微改变一下MessageSender.aidl文件:


package com.txt.remote;
import com.txt.remote.MessageModel;

interface MessageSender {
    void sendMessage(in MessageModel model);
    void setBinder(in IBinder client);
}


增加了一个setBinder的方法,为了能让客户端把Binder对象传递给服务端。


于是在服务端生成MessageSender的Binder对象的时候,增加了这个方法的复写,在服务端RemoteService的onBind方法中:


public IBinder onBind(Intent intent) {
        Log.i(tag, "onBind");
        return new MessageSender.Stub() {
            @Override
            public void sendMessage(MessageModel model) throws RemoteException {
                System.out.println("from:" + model.getFrom() + ", to: " + model.getTo()
                        + ", content:" + model.getContent());
            }
            @Override
            public void setBinder(IBinder client) throws RemoteException {
                client.linkToDeath(mRecipient,0);
            }
        };
    }


private IBinder.DeathRecipient mRecipient = new IBinder.DeathRecipient() {
        @Override
        public void binderDied() {
            Log.i(tag,"client is died");
        }
    };


setBinder的client对象就是客户端传递过来的,mRecipient是在服务端创建的对象,将这两个关联。一旦客户端进程销毁了,就会回调binderDied。


好了,上面把服务的逻辑处理完成了,那么来修改客户端,在LocalActivity中,创建一个Binder对象:


private IBinder client = new Binder();


在ServiceConnection的onServiceConnected的回调中,拿到服务端的MessageSender对象,通过setBinder方法,把这个client传递出去:


public void onServiceConnected(ComponentName name, IBinder service) {
                Log.i(tag, "onServiceConnected");
                mMessageSender = MessageSender.Stub.asInterface(service);
                try {
                    mMessageSender.asBinder().linkToDeath(mDeathRecipient,0);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
                try {
                    mMessageSender.setBinder(client);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }


至此,就完成了,双向的binder的检测,来看下测试结果。连接后手动kil掉客户端进程。

07-20 17:03:25.573 2455-2455/com.txt.mydemo:remote I/RemoteService: onStartCommand
07-20 17:03:35.244 2455-2470/com.txt.mydemo:remote I/RemoteService: LocalActivity binder is died



其实在客户端bind服务端的过程中,有一个ServiceConnection实例,该实例被传入到bindService()这个方法内,该实例在客户端实现,有两个回调方法:


connection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                Log.i(tag, "onServiceConnected");
                mMessageSender = MessageSender.Stub.asInterface(service);
            }

            @Override
            public void onServiceDisconnected(ComponentName name) {
                Log.i(tag, "onServiceDisconnected");
            }
        };
        bindService(new Intent(this, RemoteService.class), connection, Context.BIND_AUTO_CREATE);


在绑定成功可以从onServiceConnected方法中获得服务端的Binder对象;


而onServiceDisconnected是在服务端异常结束(内存不错,主动kill时)会被回调,正常解除unbind的时候是坚决不会回调的。



  1. 在服务端的onBind中校验自定义permission,如果通过了我们的校验,正常返回Binder对象,校验不通过返回null,返回null的情况下客户端无法绑定到我们的服务;
  2. 在服务端的onTransact方法校验客户端包名,不通过校验直接return false,校验通过执行正常的流程。



5、一般我们会在一个工程里面,通过声明组件的android:proccess=":remote",来把当前组件放置到一个新的进程里面。可是我们整个工程里面的manifest.xml里面就声明了一个Application类(定义多个会报错),而多进程肯定是会存在多个Application实例。所以我们在定义一个MyApplication的时候,该类的onCreate的方法就会被多次调用,那么这个时候我们可以根据进程名区分,在不同的进程onCreate的时候处理相应的逻辑。(疑问:是多次回调onCreate,还是说每个进程都会对应一个Application实例,那么多个application实例怎么会使用同一个MyApplication呢?可以联想下activity,一个activity在xml注册成标准模式,我们在代码里创建的activity的类名肯定是和xml中对应的,不可能去创建多个相同的类吧,那么标准模式下的activity,在启动的时候可以出现多个实例。就好像现在我们在xml中声明了一个application,这个application肯定是和我们的代码中的application类名一致,一一对应的,我们平时单进程的情况,整个声明周期中就只会出现一个application实例,而现在是多进程,那么会出现多个application实例对象。就好比标准模式下的activity实例对象。所以我们可以根据进程的名称是在同一个类里面分别处理当前进程对应下的application实例对象里的业务逻辑)有点啰嗦,只是突然一下想不通了。


再说说Application:


和四大组件一样,application的创建是由系统来执行的,那么必然需要在AndroidManifest.xml中来声明。如果我们只是在工程里面创建一个MyApplication类并继承自Application。相信系统是不会为我们创建这个实例的,因为我们没有声明。如果想让系统来执行MyApplication的创建,则必须把我们创建的这个类声明在xml的<application>节点下才行。比如在xml中声明MyApp:


<application
        android:name=".MyApp"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">

android:name 就是自定义的Application的全路径(包名+类名,这里省略了包名)



apk启动的时候,MyApp打印出: 

 

   I/MyApp: onCreate: process name = com.txt.mydemo 
 

  启动一个远程service的时候出现,该service设置了android:process = “:remote”: 

 

  I/MyApp: onCreate: process name = com.txt.mydemo:remote

可以看到同样是MyApp类,执行了两次onCreate方法,每个MyApp对象存在的进程不一样。


所以在多进程的情况下,我们需要在application里面根据不同的进程id识别进行不用的逻辑处理,比如在onCreate这里面初始化了一个单例对象,每个进程都是初始化一个单例对象,要看看是否有必要其他进程也需要创建单例对象。