上一篇说到aidl实现进程间通信,在最后却出现了解注册失败的情况,那么,这篇文章就把aidl实现进程间通信需要注意的事情介绍一下。
接注册失败原因
在进程间通信的时候,Binder会把客户端传递过来的对象重新转化并生成一个新的对象。也就是说,在操作的时候虽然都是listener但是这两个listener并不是同一个对象,所以在mListenerList.contains时会返回false。因为对象是不能直接的跨进程通信的。
那看看解决方案吧
RemoteCallbackList
书上是这么说RemoteCallbackList的:
RemoteCallbackList是系统专门提供的用于删除跨进程listener的接口。RemoteCallbackList是一个泛型,支持管理任意的AIDL接口。
关于RemoteCallbackList的工作原理:
在RemoteCallbackList内部有一个Map结构专门用来保存所有的AIDL回调,这个Map的key是IBinder类型,value是Callback类型。其中Callback中封装了真正的远程listener。当客户端注册listener的时候,它会把这个listener的信息存入mCallbacks中,其中key和value分别通过下面的方式获取
|
|
也就是说虽然多次跨进程传输客户端的同一个对象会在服务端生成不同的对象,但是这些新生成的对象都有一个共同点,那就是它们的底层Binder是同一个对象,这样我们就可以在客户端解注册的时候,遍历服务端所有的listener,找出那个和解注册listener具有相同Binder对象的服务端listener,并把它删除,这就是RemoteCallbackList为我们做的事情。同时RemoteCallbackList还有一个很有用的功能,那就是当客户端进程终止以后,它能够自动移除客户端所注册的listener。另外RemoteCallbackList内部自动实现了线程同步的功能,所以我们使用它来注册和解注册时,不需要做额外的线程同步工作。
听起来很枯燥。但是这些知识点还是要记得。
看一下RemoteCallbackList的使用。
对BookManagerService.java修改
使用RemoteCallbackList代替CopyOnWriteArrayList
使用RemoteCallbackList的方式来注册和解注册
|
|
使用RemoteCallbackList的遍历方式
|
|
ok这就完成了。运行程序会发现在解注册成功。
说一下使用RemoteCallbackList的注意点。
RemoteCallbackList不是一个list无法像使用list一样遍历它。必须按照下面的方式遍历。其中beginBroadcast和finishBroadcast必须要配套使用,哪怕只是为了获取RemoteCallbackList的元素个数,这个一定要注意
|
|
对AIDL总结一下:
- 功能强大,支持一对多的并发通信,支持实时通信。就是很屌很炫很牛逼
- 使用有点复杂(但是可以用起来还是很顺手啊)
- 牛逼的观察者模式
还有一点注意点:
客户端调用远程服务的方法,被调用的方法运行在服务端的Binder线程池中,同时客户端线程会被挂起,这个时候如果服务端的方法比较耗时,那么就会导致客户端线程长时间的阻塞,而这个时候客户端的线程是UI线程的话,就会ANR。例如客户端调用服务端的getBookList方法在UI线程中,如果getBookList方法是个耗时方法,那么客户端就会ANR,我们需要做的是把可能耗时的操作放在一个非UI线程。同理,如果服务端调用客户端时也需要注意避免ANR发生。
还有一点要说的:
Binder是可能会意外死亡的,这往往是由于服务端进程意外停止了,这时我们需要重新链接服务。有两种方法:
- 给Binder设置DeathRecipient监听,当Binder死亡时,我们会收到binderDied方法的回调,在binderDied方法中我们可以重新链接服务
- 在onServiceDisconnected中重新链接服务
DeathRecipient示例:
在客户端添加如下代码
再啰嗦几句。
有没有想过这个问题:服务端开启了服务,可以对应很多个客户端。但是服务端这样来者不拒真的好吗?
显然这样做的话在数据安全上是肯定不会同意的。那么怎么限制客户端的访问呢?
下面介绍一下权限验证
第一种验证方式:onBind中验证
如果验证不通过就直接返回null,这样验证失败的客户端就无法绑定服务。
使用permission
在服务端AndroidMenifest中申明权限限制:
然后在service的onBind方法中判断是否有权限
|
|
然后在客户端中申请权限
|
|
这样就可以添加限制了。
还有一种onTransact验证方式,这里就不介绍了。