binder學習

C++層

我們開發時所見到的Binder是Android系統提供給我們的java接口,java層的Binder對象只是Android對底層Binder的一個封裝,提供給上層開發人員使用,真正的Binder其實隱藏在系統底層,默默的替我們進行着跨進程通信。

Java層的服務端Binder對象在C++層對應的對象爲BBinder,而客戶端拿到的BinderProxy對象對應的則爲BpBinder,BBindder和BpBinder都繼承自IBinder,上層Binder和BinderProxy之間的通信其實是BBinder和BinderProxy之間的通信。

Binder的核心是Binder Driver,即Binder驅動,雖然各個進程不能共享自己的內存空間,但是系統的內核空間是共享的,每個進程都可以訪問,而Binder Driver即存在於內核空間,BBinder和BpBinder都是通過它來通信的,所以可以把Binder驅動當作是BBinder和BpBinder之間的一座橋樑。當然BBinder和BpBinder也不是直接和Binder驅動打交道,它們中間還隔着一個IPCThreadState。每一個進程都有一個ProcessState對象,它負責打開Binder驅動,這樣該進程中的所有線程都可以通過Binder驅動通信,而每個線程都會有一個IPCThreadState對象,它纔是真正讀寫Binder驅動的主角,它通過talkWithDriver()和驅動打交道,將IPC數據寫入驅動中。

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驅動就像所有進程共同的地盤,系統服務可以這裏留下自己的地址,而客戶端可以可以根據這些地址找到對應的服務,相互通信,從而千里姻緣一線牽,百年恩愛雙心結。




aidl文件編譯後的文件結構
















客戶端使用asInterface進程判斷



判斷是本地進程還有remote進程



若是遠程的



使用
mRemote.transact(Stub.TRANSACTION_helo_data_reply0);

進入Binder.java



調用onTransact







發佈了39 篇原創文章 · 獲贊 5 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章