转载请注明出处:http://blog.csdn.net/wu371894545/article/details/54962860
一、Binder机制概述
在Android开发中,很多时候我们需要用到进程间通信,所谓进程间通信,实现进程间通信的机制有很多种,比如说socket、pipe等,Android中进程间通信的方式主要有三种:
1.标准linux Kernel IPC 接口;
2.标准D-BUS接口;
3.Binder接口。
其中,Binder机制是使用最且最被认可的,因为Binder机制有以下优点:
1.相对于其它IPC机制,Binder机制更加简洁和快速;
2.消耗的内存相对更少;
3.传统的IPC机制可能会增加进程的开销,以及出现进程过载和安全漏洞,Binder机制则有效避免和解决了这些问题。
Binder机制是Android系统的核心机制,几乎贯穿于整个Android系统,Android系统基本上可以看作是一个基于binder通信机制的C/S架构,Binder就像网络,把Android系统的各个部分连接到了一起。利用Binder机制,可以实现以下功能:
1.用驱动程序来推进进程间通信;
2.通过共享内存来提高性能;
3.为进程请求分配每个进程的线程池;
4.针对系统中的对象引入了引用计数和跨进程的对象引用映射;
5.进程间同步调用。
1. 直观来说,Binder是Android中的一个类,它继承了IBinder接口
2. 从IPC角度来说,Binder是Android中的一种跨进程通信方式,Binder还可以理解为一种虚拟的物理设备,它的设备驱动是/dev/binder,该通信方式在Linux中没有
3. 从Android Framework角度来说,Binder是ServiceManager连接各种Manager(ActivityManager、WindowManager,etc)和相应ManagerService的桥梁
4. 从Android应用层来说,Binder是客户端和服务端进行通信的媒介,当你bindService的时候,服务端会返回一个包含了服务端业务调用的Binder对象,通过这个Binder对象,客户端就可以获取服务端提供的服务或者数据,这里的服务包括普通服务和基于AIDL的服务
Android中有大量的CS(Client-Server)应用方式,这就要求Android内部提供IPC方法,而linux所支持的进程通信方式有两个问题:性能和安全性。
目前linux支持的IPC包括传统的管道,System V IPC(消息队列/共享内存/信号量),以及socket,但只有socket支持Client-Server的通信方式,由于socket是一套通用的网络通信方式,其传输效率低下切有很大的开销,比如socket的连接建立过程和中断连接过程都是有一定开销的。消息队列和管道采用存储-转发方式,即数据先从发送方缓存区拷贝到内核开辟的缓存区中,然后再从内核缓存区拷贝到接收方缓存区,至少有两次拷贝过程。共享内存虽然无需拷贝,但控制复杂,难以使用。
在安全性方面,Android作为一个开放式,拥有众多开发者的的平台,应用程序的来源广泛,确保智能终端的安全是非常重要的。终端用户不希望从网上下载的程序在不知情的情况下偷窥隐私数据,连接无线网络,长期操作底层设备导致电池很快耗尽等等。传统IPC没有任何安全措施,完全依赖上层协议来确保。首先传统IPC的接收方无法获得对方进程可靠的UID/PID(用户ID/进程ID),从而无法鉴别对方身份。Android为每个安装好的应用程序分配了自己的UID,故进程的UID是鉴别进程身份的重要标志。使用传统IPC只能由用户在数据包里填入UID/PID,但这样不可靠,容易被恶意程序利用。可靠的身份标记只有由IPC机制本身在内核中添加。其次传统IPC访问接入点是开放的,无法建立私有通道。比如命名管道的名称,system V的键值,socket的ip地址或文件名都是开放的,只要知道这些接入点的程序都可以和对端建立连接,不管怎样都无法阻止恶意程序通过猜测接收方地址获得连接。
基于以上原因,Android需要建立一套新的IPC机制来满足系统对通信方式,传输性能和安全性的要求,这就是Binder。Binder基于 Client-Server通信模式,传输过程只需一次拷贝,为发送发添加UID/PID身份,既支持实名Binder也支持匿名Binder,安全性高。
二、从自动生成的AIDL文件中分析Binderpackage com.ryg.chapter_2.aidl;
public interface IBookManager extends android.os.IInterface {
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements
com.ryg.chapter_2.aidl.IBookManager {
PRivate static final java.lang.String DESCRIPTOR = "com.ryg.chapter_2.aidl.IBookManager";
/** Construct the stub at attach it to the interface. */
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.ryg.chapter_2.aidl.IBookManager
* interface, generating a proxy if needed.
*/
public static com.ryg.chapter_2.aidl.IBookManager asInterface(
android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.ryg.chapter_2.aidl.IBookManager))) {
return ((com.ryg.chapter_2.aidl.IBookManager) iin);
}
return new com.ryg.chapter_2.aidl.IBookManager.Stub.Proxy(obj);
}
@Override
public android.os.IBinder asBinder() {
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_getBookList: {
data.enforceInterface(DESCRIPTOR);
java.util.List<com.ryg.chapter_2.aidl.Book> _result = this
.getBookList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addBook: {
data.enforceInterface(DESCRIPTOR);
com.ryg.chapter_2.aidl.Book _arg0;
if ((0 != data.readInt())) {
_arg0 = com.ryg.chapter_2.aidl.Book.CREATOR
.createFromParcel(data);
} else {
_arg0 = null;
}
this.addBook(_arg0);
reply.writeNoException();
return true;
}
case TRANSACTION_registerListener: {
data.enforceInterface(DESCRIPTOR);
com.ryg.chapter_2.aidl.IOnNewBookArrivedListener _arg0;
_arg0 = com.ryg.chapter_2.aidl.IOnNewBookArrivedListener.Stub
.asInterface(data.readStrongBinder());
this.registerListener(_arg0);
reply.writeNoException();
return true;
}
case TRANSACTION_unregisterListener: {
data.enforceInterface(DESCRIPTOR);
com.ryg.chapter_2.aidl.IOnNewBookArrivedListener _arg0;
_arg0 = com.ryg.chapter_2.aidl.IOnNewBookArrivedListener.Stub
.asInterface(data.readStrongBinder());
this.unregisterListener(_arg0);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements
com.ryg.chapter_2.aidl.IBookManager {
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
@Override
public android.os.IBinder asBinder() {
return mRemote;
}
public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}
@Override
public java.util.List<com.ryg.chapter_2.aidl.Book> getBookList()
throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<com.ryg.chapter_2.aidl.Book> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getBookList, _data,
_reply, 0);
_reply.readException();
_result = _reply
.createTypedArrayList(com.ryg.chapter_2.aidl.Book.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public void addBook(com.ryg.chapter_2.aidl.Book book)
throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((book != null)) {
_data.writeInt(1);
book.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
@Override
public void registerListener(
com.ryg.chapter_2.aidl.IOnNewBookArrivedListener listener)
throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeStrongBinder((((listener != null)) ? (listener
.asBinder()) : (null)));
mRemote.transact(Stub.TRANSACTION_registerListener, _data,
_reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
@Override
public void unregisterListener(
com.ryg.chapter_2.aidl.IOnNewBookArrivedListener listener)
throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeStrongBinder((((listener != null)) ? (listener
.asBinder()) : (null)));
mRemote.transact(Stub.TRANSACTION_unregisterListener,
_data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
}
static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_registerListener = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
static final int TRANSACTION_unregisterListener = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
}
public java.util.List<com.ryg.chapter_2.aidl.Book> getBookList()
throws android.os.RemoteException;
public void addBook(com.ryg.chapter_2.aidl.Book book)
throws android.os.RemoteException;
public void registerListener(
com.ryg.chapter_2.aidl.IOnNewBookArrivedListener listener)
throws android.os.RemoteException;
public void unregisterListener(
com.ryg.chapter_2.aidl.IOnNewBookArrivedListener listener)
throws android.os.RemoteException;
}(1):首先,它申明了俩个方法getBookList和addBook,显然这就是我们在IbookManager.aidl中所申明的方法。同时它还俩个整形的id分别用于表示这俩个方法。(2):接着,它申明了一个内部类Stub,这个Stub就是一个Binder类,当客户端和服务端都位于同一个进程时,方法调用不会走跨进程的transact过程,而当俩者位于不同进程时,方法调用走transact过程,这个逻辑是由于Stub的内部代理类Proxy来完成。这么看来,IbookManager这个接口的确很简单,但是我们也应该认识到,这个接口的核心实现就是它的内部类Stub和Stub的内部代理类Proxy.DESCRIPTORBinder的唯一标识,一般用当前Binder的类名表示,比如本例中的com.ryg.chapter_2.aidl.IBookManagerasInterface用于将服务端的Binder对象转换成客户端所需的AIDL接口对象的类型,这种转换过程是区分进程的,如果客户端和服务端位于同一进程,那么此方法返回的就是服务端的Stub对象本身,否则返回的就是系统封装后的Stub.proxy对象。asBinder此方法返回当前Binder对象onTransact这个方法运行在服务端中的Binder线程池中,当客户端发起跨进程请求时,远程请求会通过底层封装后交由此方法来处理。服务端通过code可以确定客户端所请求的目标到底是什么。当目标方法执行完毕后,就向reply中写入返回值。需要注意的是,如果此方法返回false,那么客户端的请求会失败,因此我们可以利用这个特性来进行权限认证,毕竟我们也不希望随便一个进程都能远程调用我们的服务。虽然我们知道了BInder的工作机制,但是还需要说明俩点:1.当客户端发起远程请求时,由于当前线程会被挂起直至服务端进程返回数据,所以如果一个远程方法很耗时,那么不能在UI线程中发起此远程请求。2.由于服务器的Binder方法运行在Binder线程池,所以Binder方法不管是否耗时都应该采用同步的方式去实现。,因为它已经运行在一个线程中了。四、Binder机制的工作流程
从上面我们可以知道Binder工作机制的流程:
1.客户端获取服务端的带来对象(proxy)。我们需要明确的是客户端进程并不能直接操作服务端中的方法,如果要操作服务端中的方法,那么有一个可行的解决方法就是在客户端建立一个服务端进程的代理对象,这个代理对象具备和服务端进程一样的功能,要访问服务端进程中的某个方法,只需要访问代理对象中对应的方法即可;
2.客户端通过调用代理对象向服务端发送请求。
3.代理对象将用户请求通过Binder驱动发送到服务器进程;
4.服务端进程处理客户端发过来的请求,处理完之后通过Binder驱动返回处理结果给客户端的服务端代理对象;
5.代理对象将请求结果进一步返回给客户端进程。
通过以上5个步骤,就完成了一次Binder通信。
下面我们给出一张Binder的工作机制图 源码下载
新闻热点
疑难解答