C++層
我們開發時所見到的Binder是Android系統提供給我們的java接口,java層的Binder對象只是Android對底層Binder的一個封裝,提供給上層開發人員使用,真正的Binder其實隱藏在系統底層,默默的替我們進行着跨進程通信。
Java層的服務端Binder對象在C++層對應的對象爲BBinder,而客戶端拿到的BinderProxy對象對應的則爲BpBinder,BBindder和BpBinder都繼承自IBinder,上層Binder和BinderProxy之間的通信其實是BBinder和BinderProxy之間的通信。
Java-->C++對象轉換
回到我們上面的Demo,客戶端連接成功後拿到了BinderProxy對象,那麼這個服務端的Binder對象是如何轉爲BinderPrxoy的呢?這點我們要看bindService的源碼,bindService流程很長,感興趣的讀者可以自己去看後者直接看老羅的分析,我這裏直接告訴讀者大致過程:
1.客戶端請求bindService,先會請求ActivityManagerService;
2.ActvityManagerService再去找到對應的Service,讓Service所在進程創建並啓動Service;
3.Service調用AMS.publishService()將Binder對象傳遞給AMS;
4.AMS拿到的Binder對象同樣爲BinderProxy對象,然後調用 c.conn.connected(r.name, service)方法,將BinderProxy對象傳遞給客戶端。
這裏要知道的是,AMS也是處在一個單獨的進程中,所以Binder對象不是直接返回給AMS,AMS也不是直接返回給客戶端的,而是經過了Binder驅動。服務端Service將Binder對象傳遞給AMS時,會調用AMS在服務端的代理對象ActivityManagerProxy.publishService()方法,準確的說,Service端在此時成了AMS的客戶端。
public void publishService(IBinder token,
Intent intent, IBinder service) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeStrongBinder(token);
intent.writeToParcel(data, 0);
data.writeStrongBinder(service);
mRemote.transact(PUBLISH_SERVICE_TRANSACTION, data, reply, 0);
reply.readException();
data.recycle();
reply.recycle();
}
該方法中通過Parcel.writeStrongBinder(),將服務端的Binder對象轉化成C++中的flat_binder_object對象,並將Binder對象的地址賦值給flat_binder_object對象中的cookie。
struct flat_binder_object {
unsigned long type;
unsigned long flags;
union {
void *binder; /* local object */
signed long handle; /* remote object */
};
void *cookie;
};
flat_binder_object對象最終被包裝在了data中,然後通過mRemote.transact(),將data數據傳送到IPCThreadState, IPCThreadState再次將data包裝成binder_transaction_data,並調用talkWithDriver(),將包裝好的數據傳遞給Binder驅動。
Binder驅動收到數據後,並不會急着將數據傳給AMS,因爲傳送的數據中有flat_binder_object,所以它會查詢flat_binder_object對應的Binder對象在驅動中是否有Binder節點,如果沒有,則會利用flat_binder_object中的cookie值(指向BBinder)創建一個Binder節點,併爲該節點生成一個索引值,將索引值賦給flat_binder_object的handle。
然後AMS就可以取數據了,AMS同樣有一個IPCThreadState對象,Binder驅動將數據傳遞給該對象,接收端拿到了數據後,會調用Parcel.readStrongBinder(),這在java層是一個jni方法,該方法會先調用Native層的readStrongBinder(),利用flat_binder_object中的handle值創建BpBinder對象,然後該Native方法返回BpBinder的指針,jni方法再根據BpBinder指針創建java層的BinderProxy對象並返回給java層。至此BpBinder和BinderProxy對象都已經創建完畢。
BpBinder與BBinder的通信
上面我們已經拿到了BpBinder,那BpBinder又將如何與BBinder通信呢?這裏不得不提一個重要的變量:handle。還記得在創建BpBiner的時候,需要傳入flat_binder_object的handle嗎,而這個handle是Binder節點在驅動中的索引,即位置。這樣當BpBinder通過transact()調用BBinder時,Binder驅動就可以根據BpBinder提供的handle值找到Binder在驅動中的節點,Binder節點保存了BBinder的地址,從而找到了BBinder,實現對BBinder的調用。
系統服務的註冊
上面的例子展示的匿名Binder的通信,爲什麼說是匿名,因爲Binder對象並沒有在ContextManager中實名註冊。
ContextManager又是什麼?它是系統服務的管理者,它在java層和C++層的代理對象都爲ServiceManager,系統服務可以通過ServiceManager.addService()將自己實名註冊到ContextManager中。爲什麼要實名註冊?如果不採用實名的方法,系統服務那麼多,你難道一個個的去記得它的handle?
ServiceManager.addService()會將服務的Binder對象傳到Binder Driver,Binder Driver爲其生成Binder節點後,ContextManager就會將服務的實名和其Binder節點的索引handle保存到自身。當客戶端需要找一個系統服務時,只需將服務名ServiceManager.getService(),ServiceManager就可以找到服務的索引handle,並創建對應的BpBinder對象,從而建立通信。比如java層的ActivityManagerService就是一個Binder類,我們就可以通過ServiceManager.getService("activity"),拿到它的代理對象,從而進行Activity生命週期的調度。
上面說到ServiceManager是ContextManager的代理,因爲ContextManager本身也就是一個服務,服務端和客戶端想調用它的addService或getService時,也必須通過ServiceManager來跨進程。可是我該怎麼拿到這個代理呢?我總不能調用它的getService來獲取它自己吧。Binder這一點設計的很奇妙,ContextManager在BinderDriver中的節點索引爲0,誰讓它是老大呢。這樣大家都知道了,我想調用ContextManger,直接根據handle=0就可以生成它的代理對象BpBinder了,從而創建代理對象ServiceManager。
總結
系統中每個app和系統服務都活在自己的進程中,無法訪問彼此的內存空間,正是因爲相互通信的強烈需求,從而誕生了Binder。Binder驅動就像所有進程共同的地盤,系統服務可以這裏留下自己的地址,而客戶端可以可以根據這些地址找到對應的服務,相互通信,從而千里姻緣一線牽,百年恩愛雙心結。