Android Binder机制
一、引言
Android系统中,涉及到多进程间的通信底层都是依赖于Binder IPC机制。例如当进程A中的Activity要向进程B中的Service通信,这便需要依赖于Binder IPC。不仅于此,整个Android系统架构中,大量采用了Binder机制作为IPC(进程间通信)方案。
进程间通信还有诸如管道、SystemV、Socket等方式,那么Binder机制相比其他的优势在哪里:
1、性能:在Android设备上广泛地使用跨进程通信对性能有严格的要求。Binder相对出传统的Socket方式,更加高效。Binder数据拷贝只需要一次,而管道、消息队列、Socket都需要2次,共享内存方式一次内存拷贝都不需要,但实现方式又比较复杂。
2、安全:传统的进程通信方式对于通信双方的身份并没有做出严格的验证,比如Socket通信ip地址是客户端手动填入,很容易进行伪造,而Binder机制从协议本身就支持对通信双方做身份校检,因而大大提升了安全性。
二、Binder通信模型
Binder框架定义了四个角色:
Server:提供服务的进程。
Client:使用服务的进程。
ServiceManager :ServiceManager的作用是将字符形式的Binder名字转化成Client中对该Binder的引用,使得Client能够通过Binder名字获得对Server中Binder实体的引用。
Binder驱动:驱动负责进程之间Binder通信的建立,Binder在进程之间的传递,Binder引用计数管理,数据包在进程之间的传递和交互等一系列底层支持。
需要注意的是:
Server,Client,ServiceManager运行于用户空间,驱动运行于内核空间。这四个角色的关系和互联网类似:Server是服务器,Client是客户终端,ServiceManager 是域名服务器(DNS),驱动是路由器。
ServiceManager是一个进程,Server是另一个进程,Server向SMgr注册Binder必然会涉及进程间通信。当前实现的是进程间通信却又要用到进程间通信,这就好象蛋可以孵出鸡前提却是要找只鸡来孵蛋。这里Binder的实现比较巧妙,先假设有鸡,ServiceManager是Server端,有自己的Binder对象(实体),其它进程都是Client。ServiceManager提供的Binder比较特殊,它没有名字也不需要注册,当一个进程使用BINDER_SET_CONTEXT_MGR命令将自己注册成SMgr时Binder驱动会自动为它创建Binder实体(这就是那只预先造好的鸡)。其次这个Binder的引用在所有Client中都固定为0而无须通过其它手段获得。也就是说,一个Server若要向SMgr注册自己Binder就必需通过0这个引用号和ServiceManager的Binder通信。
三、Binder协议
Binder协议基本格式是(命令+数据),使用ioctl(fd, cmd, arg)函数实现交互。命令由参数cmd承载,数据由参数arg承载,随cmd不同而不同。下表列举了所有命令及其所对应的数据:
命令 |
含义 | arg | |
BINDER_WRITE_READ | 该命令向Binder写入或读取数据。参数分为两段:写部分和读部分。如果write_size不为0就先将write_buffer里的数据写入Binder;如果read_size不为0再从Binder中读取数据存入read_buffer中。write_consumed和read_consumed表示操作完成时Binder驱动实际写入或读出的数据个数。 |
struct binder_write_read { signed long write_size; signed long write_consumed; unsigned long write_buffer; signed long read_size; signed long read_consumed; unsigned long read_buffer; } |
|
BINDER_SET_MAX_THREADS | 该命令告知Binder驱动接收方(通常是Server端)线程池中最大的线程数。由于Client是并发向Server端发送请求的,Server端必须开辟线程池为这些并发请求提供服务。告知驱动线程池的最大值是为了让驱动发现线程数达到该值时不要再命令接收端启动新的线程。 | int max_threads; | |
BINDER_SET_CONTEXT_MGR | 将当前进程注册为SMgr。系统中同时只能存在一个SMgr。只要当前的SMgr没有调用close()关闭Binder驱动就不能有别的进程可以成为SMgr | --- | |
BINDER_THREAD_EXIT |
|
--- | |
BINDER_VERSION |
|
--- | |
四、Binder运行机制
1、注册服务(addService):Server进程要先注册Service到ServiceManager。该过程,Server是客户端,ServiceManager是服务端。
2、获取服务(getService):Client进程使用某个Service前,须先向ServiceManager中获取相应的Service。该过程,Client是客户端,ServiceManager是服务端。
3、使用服务:Client根据得到的Service信息建立与Service所在的Server进程通信的通路,然后就可以直接与Service交互。该过程,client是客户端,server是服务端。
Client,Server,Service Manager之间交互不是直接交互的,而是都通过与Binder驱动进行交互的,从而实现IPC通信方式。
注册服务和获取服务的流程涉及C的内容,能力有限,此处不做详细介绍。这里主要介绍使用服务时,Binder机制的工作原理。
1.Binder对象的获取
在Binder类中有两个方法:
public class Binder implement IBinder{
...
void attachInterface(IInterface plus, String descriptor)
IInterface queryLocalInterface(Stringdescriptor) //从IBinder中继承而来
...
}
Binder具有被跨进程传输的能力是因为它实现了IBinder接口,系统会为每个实现了该接口的对象提供跨进程传输。
Binder具有的完成特定任务的能力是通过它的IInterface的对象获得的。attachInterface方法会将(descriptor,plus)作为(key,value)对存入Binder对象中的一个Map<String,IInterface>对象中,Binder对象可通过attachInterface方法持有一个IInterface对象(即plus)的引用,并依靠它获得完成特定任务的能力。queryLocalInterface方法根据key值(即参数 descriptor)查找相应的IInterface对象。
在服务端进程,通过实现
private IBookManager.Stub mbinder = new IBookManager.Stub() {}
抽象类,获得Binder对象。
在Stub中保存了 attachInterface 对象
public Stub(){
this.attachInterface(this, DESCRIPTOR);
}
客户端获取Binder对象并获取IInterface接口对象。
在之前的AIDL部分,我们通过bindservice获得Binder对象,再通过Binder获得IInterface对象
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder binder) {
//通过服务端onBind方法返回的binder对象得到IBookManager的实例,得到实例就可以调用它的方法了
mIBookManager = IBookManager.Stub.asInterface(binder);
}
@Override
public void onServiceDisconnected(ComponentName name) {
mIBookManager = null;
}
};
其中 asInterface(binder)
方法如下:
public static com.kwmax.aidldemo.IBookManager asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.kwmax.aidldemo.IBookManager))) {
return ((com.kwmax.aidldemo.IBookManager)iin);
}
return new com.kwmax.aidldemo.IBookManager.Stub.Proxy(obj);
先通过 queryLocalInterface(DESCRIPTOR);
查找到对应的IInterface对象,然后判断对象的类型,如果是同一个进程调用则返回IBookManager对象,由于是跨进程调用则返回Proxy对象,即Binder类的代理对象。
2.调用服务端方法
获得了Binder类的代理对象,并且通过代理对象获得了IInterface对象,那么就可以调用接口的具体实现方法了,来实现调用服务端方法的目的。以addBook为例
@Override public void addBook(com.kwmax.aidldemo.Book book) throws android.os.RemoteException
{
...
mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
_reply.readException();
}
...
}
Proxy对象中的transact调用发生后,会引起系统的注意,系统意识到Proxy对象想找它的真正Binder对象(系统其实一直存着Binder和Proxy的对应关系)。于是系统将这个请求中的数据转发给Binder对象,Binder对象将会在onTransact中收到Proxy对象传来的数据,于是它从data中取出客户端进程传来的数据,又根据第一个参数确定想让它执行添加书本操作,于是它就执行了响应操作,并把结果写回reply,然后在transact
方法获得_reply
并返回结果。
case TRANSACTION_addBook:
{
data.enforceInterface(DESCRIPTOR);
com.kwmax.aidldemo.Book _arg0;
if ((0!=data.readInt())) {
_arg0 = com.kwmax.aidldemo.Book.CREATOR.createFromParcel(data);
}
else {
_arg0 = null;
}
this.addBook(_arg0);
reply.writeNoException();
return true;
}
client端:transact()发送事务请求;
server端:onTransact()接收到相应事务。
调用服务端方法时,应开启子线程,防止UI线程堵塞,导致ANR。
文章内容引用自:
https://blog.csdn.net/universus/article/details/6211589
推荐阅读
-
Android SQLite数据库版本升级的管理实现
-
Android+SQLite数据库实现的生词记事本功能实例
-
Android 解决WebView调用loadData()方法显示乱码的问题
-
Android编程布局控件之AbsoluteLayout用法实例分析
-
Android入门之Activity四种启动模式(standard、singleTop、singleTask、singleInstance)
-
Android中HTTP请求中文乱码解决办法
-
Android实现本地图片选择及预览缩放效果
-
Android编程之绝对布局AbsoluteLayout和相对布局RelativeLayout实例详解
-
Android 基于IntentService的文件下载的示例代码
-
详解Android四种存储方式