1 问题介绍
在应用S中实现一个非常基础的功能(XXInterface)。如果应用C需要用到S实现的功能。通常的做法是:在应用S中定义一个Binder对象b,b同时实现了功能接口(XXInterface)。然后将b传递给应用C,这样C就可以远程调用S中的函数。
如何将S应用的Binder传递给应用C,主要有两种方法:一种是通过ServiceManager,S将Binder对象注册到ServiceManager中,然后C通过ServiceManager获取;一种是在S中定义一个Service,在Service的onbind函数中提供Binder对象,C应用调用BindService获取Binder对象。本文主要分析BindService获取Binder的流程。
2三个Binder对象
应用S和应用C无法直接相互通信,他们的联系要通过AMS。所以BindService的过程涉及到应用(进程)S和AMS,以及应用(进程)C和AMS直接的交互。它们的通信过程涉及到了3个不同的Binder对象。
先简单介绍一下Binder的特点和这3个Binder对象。
Binder 分为proxy(Client)和stub(Service)两种,一般proxy和stub位于不同的进程,通过proxy可以调用stub实现的方法。Binder远程调用的过程其实是将参数从进程C通过Binder驱动传到S,然后执行stub中对应的方法,然后将执行结果由S传到C的过程。
Binder可以在进程之间相互传递,比如在proxy(进程C)调用stub(进程S)中的方法时,可以传入Binder类型的参数,这样Binder对象就由C传递到S。stub类型的Binder在传递到其他进程之后变为proxy类型,proxy类型传递之后仍然为proxy类型。在跨进程调用时把Binder对象作为参数,在Android中非常常见。
- 首先AMS(ActivityManagerService)本身就是一个Binder对象,他的代理对象是ActivityManagerProxy。客户端应用通过ActivityManagerProxy调用AMS的方法。比如应用C在执行BindService时,会通过ActivityManagerProxy调用AMS中的BindService方法。
- 实现了IApplicationThread接口的Binder。AMS主要通过该Binder调用客户端进程的函数。比如启动Activity时,AMS通过该Binder通知ActivityThread 创建Activity。这时 AMS是Client,ActivityThread 是Service。应用在通过ActivityManagerProxy 调用AMS的函数时,会把该Binder传递给AMS。
- 实现了IServiceConnection接口的Binder。AMS通过该Binder传递最终的Binder对象给 调用进程。该Binder也是客户进程传递给AMS的。
除了ActivityManagerProxy是从ServiceManager获取的,其他的Binder proxy 对象都是在进程间传递得到的。
3四个进程间调用
在上面的图中已经标明了这四个进程间调用。下面主要分析这四个调用传递的参数,返回的结果,涉及到的进程。
- A进程调用AMS 的BindService方法,传递的参数主要有两个 一个是Intent,包含绑定的Service信息。一个是IServiceConnection Binder对象,在AMS获得要A要的Binder(A调用BinderService要获取的Binder这里称为 Target Binder)对象之后,通过IServiceConnection Binder 将 Target Binder传递给进程A。这里还隐藏了一些细节,我们在调用Context 的BindService是,传递的是一个普通的ServiceConnection对象,并不是Binder 对象。其实是在调用AMS的BindService方法前将ServiceConnection包装为一个Binder对象IServiceConnection Binder。IServiceConnection Binder会调用ServiceConnection的onServiceConnected方法将Target Binder作为参数传递给进程A。
- AMS通过IApplicationThread Binder调用进程B的scheduleBindService方法,在scheduleBindService中会调用目标Service的onBind获取Target Binder。这里有一个问题,AMS是如何获得进程B的IApplicationThread Binder?分两种情况,1 如果进程B已经启动,会在AMS中有记录,通过A进程传给的Intent可以知道目标Service的进程,然后通过ProcessRecord可以得到IApplicationThread Binder。2 如果进程B没有启动,这时就比较复杂。通过Intent获得目标进程之后,AMS会先启动目标进程B,当B启动之后,会主动联系AMS,提供IApplicationThread Binder。详见代码。
- 进程B通过ActivityManagerProxy 调用AMS的publishService方法,将Target Binder传递给AMS。
- AMS 通过IServiceConnection 将Target Binder 传递给进程A。