初探Android中的binder机制
Binder机制是Android系统中最主要的进程间通信机制。虽然Android底层使用的是linux内核,但是除了匿名管道,socket外,Android并没有使用linux中的命名管道,信号量,消息队列等传统的IPC通信方式。Binder犹如一张大网,将Android整个系统中的组件,跨进程的组织在一起。淡化进程,强化组件是Android的一个设计理念。在这个过程中,Binder发挥了巨大作用。
binder 的作用
binder的作用可以概括为两点:
IPC:是一种跨进程通信手段,但是传输数据的大小受限制,不建议传输较大数据。
RPC:是一种远程过程调用手段,即一个进程中可以透明的调用另外一个进程中的方法。
两者经常相互伴随,例如跨进程调用的时候,参数的传递以及结果的传递等。
binder机制简述
binder实体对象: 就是binder服务的提供者,一个提供binder服务的类必须继承BBinder类(native层),因此binder实体对象又叫做BBinder对象。
binder引用对象: 是binder服务提供者在客户端进程的代表,每个引用对象类型必须继承BpBinder类(native层),因此binder引用对象有叫做BpBinder对象。
binder代理对象: 代理对象简单理解为内聚了一个binder引用对象,因此可以通过这个内聚的引用对象发起RPC调用。可以有多个不同的代理对象,但却内聚了同一个引用对象。
IBinder对象: BBinder和BpBinder都是继承自IBinder.因此binder实体对象和binder引用对象都可以称为IBinder对象。可以通过IBinder.queryLocalInterface()方法来判断到底是binder实体对象还是binder引用对象。
binder跨进程传输的数据类型是Parcel。
以RPC为例的示意图如下:
android_binder-1.png 通过一个运行在内核空间的binder驱动进程,将两个用户空间的进程联系了起来。为解决由于用户空间进程之间虚拟地址相互独立而引起的无法跨进程调用别的进程中的对象方法的难题带来了曙光。
通过BInder引用对象发起RPC调用
假设App中的MainActivity中以startActivityForResult()方法启动了该App中的Main2Activity,那么Main2Activity可以通过AMS这个binder服务中的getCallingActivity()方法查询是谁启动了自己。
AMS.getCallingActivity():
public ComponentName getCallingActivity(IBinder token)
去参数是Activity.mToken. 可以通过反射从客户端Activity组件中获取。
1). 获得AMS的引用binder
AMS作为一个系统服务,在Android系统启动过程中,会将自己注册到ServiceManager中(注册过程以后在分析)。现在只要知道客户端可以通过ServiceManager来获得AMS的引用binder即可。
2). 使用引用binder进行RPC调用时,需要直到要调用的方法的编号,这个编号可以从Android源码中获取。
在Android 6.0 中该方法编号如下:
int GET_CALLING_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+21;
服务端根据这个编号,执行相应的逻辑。 3). 方法参数的传递
binder中唯一能的传递的数据结构就是Parcel,所以必须将方法的参数打包到Parcel中。针对不同的数据类型Parcle提供了不同的方法,来将这些对应的数据打入Parcel中。
另外还要准备一个Parcel用于接收服务端的返回数据。
4). 发起RPC调用
通过引用对象的transact()方法,发起RPC调用,绝大多数情况下,此调用是一个同步调用,也就是说会一直阻塞到服务端将数据返回为止。
但是当transact()方法中传入的flag为FLAG_ONEWAY时,方法会立即返回,不会等到服务端返回数据。
该方法会最终将上述信息传入binder驱动中去。
5). 客户端从返回的Parcel中读取数据
服务端中将请求的方法执行完毕之后,将方法返回值打入到parcel中,binder驱动将其返回到客户端组件中,然后客户端组件按照调用方法的返回值类型,从返回的parcel中读取即可。
publicclassMain2ActivityextendsAppCompatActivity{
@Override
protectedvoidonCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
// 拿到AMS的引用对象