轉載請注明出處: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的工作機制圖
源碼下載
新聞熱點
疑難解答