Android Binder机制彻底梳理二
根据AIDL了解整体调用流程【重点分析AIDL流程】:
在上一次https://www.cnblogs.com/webor2006/p/11741743.html中我们已经对Android Binder机制有了一个比较深刻的认识了,最后也手动通过写了一个AIDL来实现两进程的通讯例子,这次则会更加进一步来以这个实际例子为角度进行流程分析,争取彻底将Android的Binder机制给梳理清。
对于咱们编写的AIDL跨进程的例子而言,用图来简单描述下跨进程:
对于两进程我们是用AIDL文件来进行跨进程通讯的,那这个AIDL用一个形象的例子来解释它其实可以这样来解理:假设进程A是中国人,进程B是日本人:
那两国进行通信是不是得用英语来交流,假如都只会本国语言的话,所以AIDL其实就相当于这个英语,也就是两国交流的一个工具:
而在我们编写了AIDL之后生成的代码中会看到有两个重要的东东:
此时咱们来说明一下这俩东东的含义:
每个进程都会有一个Stub和Proxy,然后我们知道跨进程访问是存在一个C/S架构的,那咱们就把进程A当C端,进程B当S端:
很显然两个进程中间隔了个墙无法直接通讯,要想通讯肯定需要有一个共享内存,如下:
其中Stub是跟服务端交互的,而Proxy是跟客户端交互的:
而当进程B启动时则会主动向共享内存那块注册一个Binder引用,里面会包含进程B的一些信息,如下:
而两进程间的通讯大致流程大概是这样,Proxy是往IBiner写数据,而Stub是从IBinder来读数据的,所以如果进程A要发消息给进程B就会是这样:
而如果服务端往客户端发消息,则类似:
好!!上面是简单对一些概念进行了一个简单图解,是不是这样在之后的分析中会得要确认滴,接下来直接来看AIDL生成的代码,先来看Stub,我们知道它是跟服务端进行通讯的:
这里有一个DESCRIPTOR的常量,它是干嘛用的呢?之后再来看,这里看一个它的构造方法调用了一个attachInterface(),将自身传进去了,先瞅一下它的实现细节:
那这记下来有啥用呢,其实注解上已经有说明了:
有点懵逼,继续来分析,我们知道在C端来绑定服务时会有这么一段代码,如下:
也就是会调到:
咱们来看一下这个方法的实现细节,其中为啥在Stub()构造中要调attachInterface就迎刃而解了,瞅瞅:
那咱们跟到queryLocalInterface方法中去瞅瞅细节:
它是接口,具体实现就是Binder,看下它的实现:
而像我们写的这个AIDL肯定不是这种情况,S端是在另一个进程,所以此时流程就会执行到这:
也就是返回了一个代理对象,里面包含IBinder,注意这个IBinder其实是:
而Proxy长啥样呢?
好,咱们来验证一下看是否拿到的就是Proxy,修改一下咱们上一次的小案例:
嗯,确实是,也就是最终咱们调用的这些:
都调用的是Proxy中的方法了:
那是不是这里也能论证之前咱们所说的理论,Proxy是跟客户端通讯的,也就是Client中调用addPerson()方法,先往IBinder来写数据,大概瞅一眼:
然后开始往Parcel对象开始写数据了:
接着就比较核心了:
一猜都能猜到,跨进程通信就靠它了,细看一下:
然后由于这个方法木有返回值所以木有返回,则看是否发生异常了:
而我们还定义了另一个AIDL方法,它是有返回值,所以在transact之后会有一个值的读取动作,瞅一下:
目前已经将数据写到IBinder了,接下来则是IBinder往服务端写数据了,此时其实流程就会到这:
这时就会调用Stub的onTransact方法了,如下:
咱们具体来看一下是怎么来执行的,首先会进行验证:
然后再调用服务端自己的方法:
,也就是咱们在服务端这里写的:
最后则写结果给IBinder:
然后客户端就可以读结果了,如下:
所以我们在调用远程的方法时可能会抛异常,在我们调用时就可以发现会要去try..catch..,如下:
以上就是关于AIDL进程间通讯的整体流程的梳理,不过这里缺少底层源代码的剖析,还不够细致,也就是说我们对于整个Binder通讯的大致流程是清楚了但是对于它在Android中的底层代码实现细节却一知半解,所以接下来则通过系统底层彻底的再来梳理一遍。
根据AIDL彻底梳理流程【分析系统源码】:
这里咱们以“android5.1.1”的源码进行分析,9.0的待之后再来进行梳理,先将低版本的原理梳理清楚,应该整个机制大体差不多,这里先提前将相关的代码导到工程中以便进行分析:
这个过程分析起来是比较复杂的,需要有耐心,理解透了对咱们的技能提升是有质的飞跃的,所以再复杂这花的时间也是值得的。那咱们从哪分析起呢?直接从我们应用的角度出发,在使用时会先bindService,如下:
所以点击进去看一下它的具体系统的实现:
我们知道最终Context的实现类是ContextImpl,所以定位到ContextImpl:
此时再定位到bindServiceCommon方法,里面代码比较多只看核心:
先来瞅一下ActivityManagerNative类:
再看一下IActivityManager接口:
跟啥类似呢?咱们来瞅一下自己编写的AIDL生成的代码:
而且还看个东东就能秒懂的:
这不也就是一个AIDL的Binder通信嘛,只是说这个是由Google工程师自己编写的,而我们使用的是编译器自动生成的,了解到这个本质非常之重要,因为我们平常使用的大量的Activity中的方法其实都是用的Binder机制来进行通讯的,比如:
其实底层是一个C/S的通讯,这个在之后的其它系统源中码会大量用到,好,下面回到主流程,再来找一下它中的getDefault()方法:
看gDefault是啥呢?瞅一下:
那看一下里面的create()的实现:
这里再说一个这个ServiceManager架构设计层面的东东,对于Android中的各个APP,每个APP都需要用到系统的一些服务,那不可能每个APP都自己去实现这些系统服务,而应该是有一个统一的系统服务器管理中心,当app需要这些服务器则通过这个管理中心去拿,画个简图如下:
而每个具体系统服务肯定是在另外单独的进程中,应用一启动都会向ServiceManager中进行注册,也就是如上一次所分析的将IBinder引用注册到了ServiceManager当中了,而当APP需要某些系统服务时,则需要通过ServiceManager.getService("xxx")来获取IBinder引用进而来实现整个跨进程的系统服务器通讯,整个就是典型的Binder调用的过程。那有了IBinder引用之后 ,则需要获取真正的对象那不还是跟咱们写的一样,如:
所以回到咱们的流程中就可以看到,获得了IBinder引用之后,则接下来就是调用它了:
好,继续回到主流程继续分析,接着就得来分析它了:
由于AcitivityManagerNative.getDefault()返回的是IActivityManager,所以得去找它的实现类,它的具体实现类其实是经典的ActivityManagerService:
所以,最终会调到这:
发现绑定的流程又要进行中断了,这里的mServices是啥呢?
所以,流程继续中转,转到ActivityServices.bindServiceLocked(),接下来流程就比较重要了,代码量也比较多,需要打起精神:
当然还是分析主流程:
也就是:
然后继续:
然后跟进去瞅一眼,看是如何绑定服务的:
其中r是ServiceRecord,app是ProcessRecord,而thread是IApplicationThread:
而它的具体实现类是在这:
所以再来找它里面的scheduleBindService()方法:
而谈到ActivityThread我们就非常之熟悉了,里面有一个mH的Handler,里面处理了大量的系统消息,这里来找一下BIND_SERVICE的消息,看它的处理细节:
好,接下来研究下它内部的细节:
啥意思,回到咱们写的案例上来理解,也就是咱们服务端编写的Service:
为啥这么肯定,咱们再来理解一下它的赋值就秒懂了:
对于服务的创建最终会执行到ActivityThread.handleCreateService()来,具体流程就不多分析了,这里来看一下我们想看的:
那很显然这个服务肯定是咱们编写的服务端的服务嘛:
到这里咱们得将之前的图修改一下:
那。。这个IBinder引用是如何返回到客户端的呢?好,接下来继续回到主流程:
也就是:
至此!!整个绑定服务及最终将IBinder引用给返回给客户端的完整流程就已经分析到了,真还是挺复杂的。。
好,接下来再来讨论一个细节问题:
这里就找到Proxy类以其中的一个方法来例:
而根据我们上一节中的底层分析,Binder驱动底层是由C++来实现的,所以肯定会用到ndk,那咱们瞅一下看是不是:
最终就会看到NDK的身影:
至于底层就不看了,这是往Binder写数据的流程,接着就调用服务端的这个回调方法,最终调用到服务端的方法,如下:
好,还有个细节就是说IBinder引用什么时候会放到共享内存呢?也就是:
其实是这样的:
然后会去调用Binder的无参构造,那就看一下它的无参构造实现:
也就是只要创建咱们的IBinder,则会通过NDK来注册到底层的Binder驱动中。整个Binder机制就分析到这了,通过这两篇的梳理,对于Android Binder机制有了一个本质认识,这对于咱们不管是面试还是说实际工作意义都是非常大的。