关于Binder看这一篇就够了

本文主要包括三个部分:1、Binder的原理,注意这部分只会涉及Java部分,不会深入到native和内核,所以会比较浅;2、Android系统服务使用Binder的原理;3、如何使用Binder,除了通用方法,还会有不使用AIDL实现,以及不使用Service获得Binder引用的方法。

一、Binder的原理

如图,Binder架构由客户端、服务端和Binder驱动构成。客户端获得Binder对象后,通过transact的方法传递参数,通过Binder驱动传给服务端。然后调用服务端的onTransact方法,接着在将得到的结果通过Binder驱动返回给客户端。

Android官方为开发者提供了一个工具AIDL(准确的说AIDL并不是工具,而是一套标准),有了这个工具我们甚至不用处理transact方法和onTransact方法,以至于我们通过Binder进行IPC时好像是在使用本地方法。 由上面浅显的原理我们可以知道,服务端的Binder才是真正在处理结果,客户端的Binder只是调用服务端而已。客户端的Binder借助Binder驱动从服务端获得,并以某种方式与服务端的Binder关联起来(涉及更深层的原理,本文就不介绍了)。也给我们带来的一个问题:客户端Binder的引用如何获得?下面的内容将会介绍,系统服务和自定义的binder分别采用不同的方式。

二、Android系统服务使用binder的原理

Android系统服务使用binder的过程与我们通过域名查询dns服务器,获得ip,最后访问网站类似。ServiceManager就像是DNS服务器,我们可以通过它获得其他服务的Binder。而这一前提是其他服务启动后要先把自己注册到ServiceManager中。

由上图可知,我们在向ServiceManager获取其他服务的Binder引用时是通过Binder进行IPC,Ams等系统服务注册时也是。从Binder的架构上来说,ServiceManager是Binder的服务端。那么ServiceManager的Binder引用要怎么获得?这不是“鸡生蛋蛋生鸡”的未解之谜吗?答案其实很简单:ServiceManager的Binder可以直接用BinderIntenal.getContextObject()获得,就像DNS服务器一样,我们是默认知道的。

1、系统服务的注册

举Ams的例子,其他大同小异:

基于Android api27,关于Framework的代码可以参考我的另一篇文章。

2、系统服务的获取

我们获取系统服务是通过Context的getSystemService方法获得,实际上是调用ContextImpl的getSystemService方法。然后会调用到SystemServiceRegistry.getSystemService()。注意:SystemServiceRegistry这个类和服务的注册没有半毛钱关系,它的功能是缓存通过ServiceManager获取服务的方法类,而这个容器是静态的(SystemServiceRegistry#SYSTEM_SERVICE_FETCHERS),所以一个进程只会保存一份,也就是说我们在不同的Context获得的对象实际是同一个。

Activity是Context的子类,更准确的说是ContextWrapper的子类,而ContextWrapper不过是个代理类。实际上Activity具体的操作都是由其成员变量mBase完成,而mBase是一个ContextImpl类(继承Context)。所以如果Context是一个Interface的话,那么这就是一个标准的静态代理模式。

三、如何使用Binder

有了AIDL我们可以轻松地使用Binder,下面将会介绍三种不同的使用方法,其中不借助AIDL的方法会让我们对Binder的原理有更深入的理解,而不使用Service获取Binder的引用更是独辟蹊径,相信会给读者一定的启发。由于这几个方法并不会很难,所以下面我只讲一些注意事项,其他的读者可以自行阅读代码。附上demo地址:github.com/jeepc/Binde… (有些地方比较简略,更加规范请参考官方文档

1、通用的方法

通用的方法就是借助AIDL,使用Service获得Binder对象。详见demo的CommonActivity。

注意事项:

  • client和server的aidl文件包名一定要相同。这样ide生成对应的java类才会位于同一包名下,jvm才会认为这是同一个类。
  • 使用自定义参数以及回调时要记得在aidl文件中import。
  • 使用自定义参数时要注意aidl文件中定义。比如XXXBean就要定义XXXBean.aidl,具体见demo。
  • 使用自定义参数时要注意自己实现“空构造方法”以及“readFromParcel”方法。
  • 要注意in、out以及inout的区别。见demo。
2、不使用AIDL

通过查看aidl生成的代码不难知道,其中proxy类用于客户端,stub用于服务端。当我们自己不借助aidl时,只需要在client实现proxy类,在server实现stub类。由于Android Framework也是借助AIDL,所以当我们在学习Framework代码时,只要发现extends IXXX.Stub,那么它就是服务端。反之,是客户端。在上一小节中,我们提到了回调,实际上在回调的时候,我们的client变换成了服务端的角色。不使用AIDL的方法详见NonuseAidlActivity。

注意事项:相关接口以及类的包名要一致。

在NonuseAidlActivity#startLocalService,我尝试了用binder调用本地服务,可以发现binder不仅可以用来IPC。本地调用其实没卵用,有点杀鸡用牛刀的感觉。但是其中涉及到的一个方法queryLocalInterface常常被我们利用来hook系统服务(见NonuseAidlProxy#INonuseAidl),如果对这方面有兴趣,可以阅读这一文章

3、不使用Service

上面两个小节,实际上我们都是用Service的回调获取Binder。这一小节我们讲的是用另一种方法获取Binder,就是用ContentProvider,这是我在RePlugin中学到的。只要你熟悉ContentProvider的使用,理解起来也很简单。详见NonuseServiceActivity。

注意事项:相关接口以及类的包名要一致。

思考:有没有其他更多获取Binder引用的方式?

4、one more thing

通过阅读demo,相信各位已经解锁了Binder的各种姿势。再提一嘴,可以通过linkToDeath监听Binder是否死亡,以及:我们知道Binder传递的参数可以为基本数据类型和实现Parcelable的类,还有就是Binder,这一点在回调中以及Framework中经常用到。

参考:

1、柯元旦《Android内核剖析》
2、Android中不使用AIDL实现Service的远程调用
3、Android Binder的设计与实现

转载于:https://juejin.im/post/5bbc600b5188255c2f42372a

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值