Binder到底是什么?
我们经常提到Binder,那么Binder到底是什么呢?
Binder的设计采用了面向对象的思想,在Binder通信模型的四个角色里面;他们的代表都是“Binder”,这样,对于Binder通信的使用者而言,Server里面的Binder和Client里面的Binder没有什么不同,一个Binder对象就代表了所有,它不用关心实现的细节,甚至不用关心驱动以及SM的存在;这就是抽象。
通常意义下,Binder指的是一种通信机制;我们说AIDL使用Binder进行通信,指的就是Binder这种IPC机制。
对于Server进程来说,Binder指的是Binder本地对象
对于Client来说,Binder指的是Binder代理对象,它只是Binder本地对象的一个远程代理;对这个Binder代理对象的操作,会通过驱动最终转发到Binder本地对象上去完成;对于一个拥有Binder对象的使用者而言,它无须关心这是一个Binder代理对象还是Binder本地对象;对于代理对象的操作和对本地对象的操作对它来说没有区别。
对于传输过程而言,Binder是可以进行跨进程传递的对象;Binder驱动会对具有跨进程传递能力的对象做特殊处理:自动完成代理对象和本地对象的转换。
面向对象思想的引入将进程间通信转化为通过对某个Binder对象的引用调用该对象的方法,而其独特之处在于Binder对象是一个可以跨进程引用的对象,它的实体(本地对象)位于一个进程中,而它的引用(代理对象)却遍布于系统的各个进程之中。最诱人的是,这个引用和java里引用一样既可以是强类型,也可以是弱类型,而且可以从一个进程传给其它进程,让大家都能访问同一Server,就象将一个对象或引用赋值给另一个引用一样。Binder模糊了进程边界,淡化了进程间通信过程,整个系统仿佛运行于同一个面向对象的程序之中。形形色色的Binder对象以及星罗棋布的引用仿佛粘接各个应用程序的胶水,这也是Binder在英文里的原意。
驱动里面的Binder
我们现在知道,Server进程里面的Binder对象指的是Binder本地对象,Client里面的对象值得是Binder代理对象;在Binder对象进行跨进程传递的时候,Binder驱动会自动完成这两种类型的转换;因此Binder驱动必然保存了每一个跨越进程的Binder对象的相关信息;在驱动中,Binder本地对象的代表是一个叫做binder_node的数据结构,Binder代理对象是用binder_ref代表的;有的地方把Binder本地对象直接称作Binder实体,把Binder代理对象直接称作Binder引用(句柄),其实指的是Binder对象在驱动里面的表现形式;读者明白意思即可。
OK,现在大致了解Binder的通信模型,也了解了Binder这个对象在通信过程中各个组件里面到底表示的是什么。
深入理解Java层的Binder
IBinder/IInterface/Binder/BinderProxy/Stub
我们使用AIDL接口的时候,经常会接触到这些类,那么这每个类代表的是什么呢?
IBinder是一个接口,它代表了一种跨进程传输的能力;只要实现了这个接口,就能将这个对象进行跨进程传递;这是驱动底层支持的;在跨进程数据流经驱动的时候,驱动会识别IBinder类型的数据,从而自动完成不同进程Binder本地对象以及Binder代理对象的转换。
IBinder负责数据传输,那么client与server端的调用契约(这里不用接口避免混淆)呢?这里的IInterface代表的就是远程server对象具有什么能力。具体来说,就是aidl里面的接口。
Java层的Binder类,代表的其实就是Binder本地对象。BinderProxy类是Binder类的一个内部类,它代表远程进程的Binder对象的本地代理;这两个类都继承自IBinder, 因而都具有跨进程传输的能力;实际上,在跨越进程的时候,Binder驱动会自动完成这两个对象的转换。
在使用AIDL的时候,编译工具会给我们生成一个Stub的静态内部类;这个类继承了Binder, 说明它是一个Binder本地对象,它实现了IInterface接口,表明它具有远程Server承诺给Client的能力;Stub是一个抽象类,具体的IInterface的相关实现需要我们手动完成,这里使用了策略模式。
AIDL过程分析
现在我们通过一个AIDL的使用,分析一下整个通信过程中,各个角色到底做了什么,AIDL到底是如何完成通信的。(如果你连AIDL都不熟悉,请先查阅官方文档)
首先定一个一个简单的aidl接口:
// ICompute.aidl
package com.example.test.app;
interface ICompute{
int add(int a , int b);
}
然后用编译工具编译之后,可以得到对应的ICompute.java类,看看系统给我们生成的代码:
package com.example.test.app;
public interface ICompute extends android.os.IInterface{
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements com.example.test.app.ICompute{
private static final java.lang.String DESCRIPTOR ="com.example.test.app.ICompute";
/**
* Construct the stub at attach it to the interface.
*/
publicStub(){
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.example.test.app.ICompute interface,
* generating a proxy if needed.
*/
public static com.example.test.app.ICompute as Interface(android.os.IBinder obj){
if((obj ==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if(((iin !=null) && (iin instanceof com.example.test.app.ICompute))) {
return((com.example.test.app.ICompute) iin);
}
return new com.example.test.app.ICompute.Stub.Proxy(obj);
}
@Override
public android.os.IBinder as Binder(){
return this;
}
@Override
public boolean onTransact (int code, android.os.Parcel data, android.os.Parcel reply, int flags ) throws android.os.RemoteException{
switch(code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_add: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
int _result =this.add(_arg0, _arg1);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.example.test.app.ICompute{
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
@Override
public android.os.IBinder as Binder(){
return mRemote;
}
public java.lang.String getInterfaceDescriptor(){
return DESCRIPTOR;
}
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
@Override
public int add(int a,int b) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try{
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(a);
_data.writeInt(b);
mRemote.transact(Stub.TRANSACTION_add, _data, _reply,0);
_reply.readException();
_result = _reply.readInt();
}finally{
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION +0);
}
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
public int add (int a,int b) throws android.os.RemoteException;
}
系统帮我们生成了这个文件之后,我们只需要继承ICompute.Stub这个抽象类,实现它的方法,然后在Service 的onBind方法里面返回就实现了AIDL。这个Stub类非常重要,具体看看它做了什么。
Stub类继承自Binder,意味着这个Stub其实自己是一个Binder本地对象,然后实现了ICompute接口,ICompute本身是一个IInterface,因此他携带某种客户端需要的能力(这里是方法add)。此类有一个内部类Proxy,也就是Binder代理对象;
然后看看asInterface方法,我们在bind一个Service之后,在onServiceConnecttion的回调里面,就是通过这个方法拿到一个远程的service的,这个方法做了什么呢?
/**
* Cast an IBinder object into an com.example.test.app.ICompute interface,
* generating a proxy if needed.
*/
public static com.example.test.app.ICompute as Interface(android.os.IBinder obj){
if((obj ==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if(((iin !=null) && (iin instanceof com.example.test.app.ICompute))) {
return((com.example.test.app.ICompute) iin);
}
return new com.example.test.app.ICompute.Stub.Proxy(obj);
}
首先看函数的参数IBinder类型的obj,这个对象是驱动给我们的,如果是Binder本地对象,那么它就是Binder类型,如果是Binder代理对象,那就是BinderProxy类型;然后,正如上面自动生成的文档所说,它会试着查找Binder本地对象,如果找到,说明Client和Server都在同一个进程,这个参数直接就是本地对象,直接强制类型转换然后返回,如果找不到,说明是远程对象(处于另外一个进程)那么就需要创建一个Binde代理对象,让这个Binder代理实现对于远程对象的访问。一般来说,如果是与一个远程Service对象进行通信,那么这里返回的一定是一个Binder代理对象,这个IBinder参数的实际上是BinderProxy;
再看看我们对于aidl的add 方法的实现;在Stub类里面,add是一个抽象方法,我们需要继承这个类并实现它;如果Client和Server在同一个进程,那么直接就是调用这个方法;那么,如果是远程调用,这中间发生了什么呢?Client是如何调用到Server的方法的?
我们知道,对于远程方法的调用,是通过Binder代理完成的,在这个例子里面就是Proxy类;Proxy对于add方法的实现如下:
@Override
public int add( int a, int b ) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try{
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(a);
_data.writeInt(b);
mRemote.transact(Stub.TRANSACTION_add, _data, _reply,0);
_reply.readException();
_result = _reply.readInt();
}finally{
_reply.recycle();
_data.recycle();
}
return _result;
}
它首先用Parcel把数据序列化了,然后调用了transact方法;这个transact到底做了什么呢?这个Proxy类在asInterface方法里面被创建,前面提到过,如果是Binder代理那么说明驱动返回的IBinder实际是BinderProxy, 因此我们的Proxy类里面的mRemote实际类型应该是BinderProxy;我们看看BinderProxy的transact方法:(Binder.java的内部类)
public native boolean transact (int code, Parcel data, Parcel reply, int flags ) throws RemoteException ;
这是一个本地方法;它的实现在native层,具体来说在frameworks/base/core/jni/android_util_Binder.cpp文件,里面进行了一系列的函数调用,调用链实在太长这里就不给出了;要知道的是它最终调用到了talkWithDriver函数;看这个函数的名字就知道,通信过程要交给驱动完成了;这个函数最后通过ioctl系统调用,Client进程陷入内核态,Client调用add方法的线程挂起等待返回;驱动完成一系列的操作之后唤醒Server进程,调用了Server进程本地对象的onTransact函数(实际上由Server端线程池完成)。我们再看Binder本地对象的onTransact方法(这里就是Stub类里面的此方法):
@Override
public boolean onTransact( int code, android.os.Parcel data, android.os.Parcel reply , int flags ) throws android.os.RemoteException {
switch(code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_add: {
data.enforceInterface(DESCRIPTOR);
int_arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
int _result =this.add(_arg0, _arg1);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
在Server进程里面,onTransact根据调用号(每个AIDL函数都有一个编号,在跨进程的时候,不会传递函数,而是传递编号指明调用哪个函数)调用相关函数;在这个例子里面,调用了Binder本地对象的add方法;这个方法将结果返回给驱动,驱动唤醒挂起的Client进程里面的线程并将结果返回。于是一次跨进程调用就完成了。
至此,你应该对AIDL这种通信方式里面的各个类以及各个角色有了一定的了解;它总是那么一种固定的模式:一个需要跨进程传递的对象一定继承自IBinder,如果是Binder本地对象,那么一定继承Binder实现IInterface,如果是代理对象,那么就实现了IInterface并持有了IBinder引用;
Proxy与Stub不一样,虽然他们都既是Binder又是IInterface,不同的是Stub采用的是继承(is 关系),Proxy采用的是组合(has 关系)。他们均实现了所有的IInterface函数,不同的是,Stub又使用策略模式调用的是虚函数(待子类实现),而Proxy则使用组合模式。为什么Stub采用继承而Proxy采用组合?事实上,Stub本身is一个IBinder(Binder),它本身就是一个能跨越进程边界传输的对象,所以它得继承IBinder实现transact这个函数从而得到跨越进程的能力(这个能力由驱动赋予)。Proxy类使用组合,是因为他不关心自己是什么,它也不需要跨越进程传输,它只需要拥有这个能力即可,要拥有这个能力,只需要保留一个对IBinder的引用。如果把这个过程做一个类比,在封建社会,Stub好比皇帝,可以号令天下,他生而具有这个权利(不要说宣扬封建迷信。。)如果一个人也想号令天下,可以,“挟天子以令诸侯”。为什么不自己去当皇帝,其一,一般情况没必要,当了皇帝其实限制也蛮多的是不是?我现在既能掌管天下,又能不受约束(Java单继承);其二,名不正言不顺啊,我本来特么就不是(Binder),你非要我是说不过去,搞不好还会造反。最后呢,如果想当皇帝也可以,那就是asBinder了。在Stub类里面,asBinder返回this,在Proxy里面返回的是持有的组合类IBinder的引用。
再去翻阅系统的ActivityManagerServer的源码,就知道哪一个类是什么角色了:IActivityManager是一个IInterface,它代表远程Service具有什么能力,ActivityManagerNative指的是Binder本地对象(类似AIDL工具生成的Stub类),这个类是抽象类,它的实现是ActivityManagerService;因此对于AMS的最终操作都会进入ActivityManagerService这个真正实现;同时如果仔细观察,ActivityManagerNative.java里面有一个非公开类ActivityManagerProxy, 它代表的就是Binder代理对象;是不是跟AIDL模型一模一样呢?那么ActivityManager是什么?他不过是一个管理类而已,可以看到真正的操作都是转发给ActivityManagerNative进而交给他的实现ActivityManagerService 完成的。
OK,本文就讲到这里了,要深入理解Binder,需要自己下功夫;那些native层以及驱动里面的调用过程,用文章写出来根本没有意义,需要自己去跟踪;接下来你可以:
看Android文档,Parcel, IBinder, Binder等涉及到跨进程通信的类;
不依赖AIDL工具,手写远程Service完成跨进程通信
看老罗的博客或者书(书结构更清晰)
学习Linux系统相关知识;自己看源码。
网友评论