關鍵詞:Binder / 跨進程通信機制 / AIDL /
Binder 是系統各個組件的橋梁,是一種極其方便的跨進程通信機制。Android 的四大組件、AMS、PMS 等系統服務都與 Binder 有關系。
這篇筆記是看《Android 開發藝術探索》之后,對 Binder 的進一步學習,對 Binder 知識的梳理參考了以下幾篇非常優秀的文章(感謝存在那么多大神對知識的無私傳播):(1)Weishu’s Notes - Binder 學習指南 (2)Android Binder 設計與實現 - 設計篇 (3)Android 進程間通信(ipC)機制 Binder 簡要介紹和學習計劃 (4)Android 跨進程通信之使用 AIDL 等等不一一列舉。
AIDL (Android Interface Definition Language,安卓接口定義語言) 是一種 IDL 語言,用于生成可以在 Android 設備上兩個進程之間進行進程間通信(interPRocess communication, IPC)的代碼。如果在一個進程中(例如 Activity)要調用另一個進程中(例如 Service)對象的操作,就可以使用 AIDL 生成可序列化的參數。AIDL IPC 機制是面向接口的,像 COM 或 Corba 一樣,但是更加輕量級。它是使用代理類在客戶端和實現端傳遞數據。
[ 關于 AIDL 需要知道的幾點 ]
使用 AIDL 之前需要理解如何使用 bindService;在 Android 上,一個進程不能正常訪問另一個進程的內存,需要將它們的對象分解成操作系統所理解的基本單位,按次序跨越進程邊界,AIDL 就起到這個作用;AIDL 允許你定義客戶端與服務端達成一致的程序接口,使用進程間通信來相互交流;什么時候使用 AIDL: 如果不需要執行不同應用之間的 IPC 并發,應該使用 Binder 建立接口;如果想執行 IPC,但是不需要處理多線程,應該使用 Messenger 實現接口;而使用 AIDL 的必要情況是:允許來自不同應用的客戶端跨進程通信來訪問你的 Service,而且想要在你的 Service 中處理多線程;AIDL 的原理:實際上就是通過我們寫的 aidl 文件,幫助我們生成了一個接口,一個 Stub 類用于服務端,一個 Proxy 類用于客戶端調用。我們使用AIDL接口的時候,經常會接觸到 IBinder / IInterface / Binder / BinderProxy / Stub 這些類,那么這每個類代表的是什么呢?
IBinder 是一個接口,它代表了一種跨進程傳輸的能力;只要實現了這個接口,就能將這個對象進行跨進程傳遞;這是驅動底層支持的;在跨進程數據流經驅動的時候,驅動會識別 IBinder 類型的數據,從而自動完成不同進程的 Binder 本地對象以及 Binder 代理對象的轉換。IBinder 負責數據傳輸。這里的 IInterface 代表的就是遠程 server 對象具有什么能力。具體來說,就是 aidl 里面的接口。java 層的 Binder 類,代表的其實就是 Binder 本地對象。BinderProxy 類是 Binder 類的一個內部類,它代表遠程進程的 Binder 對象的本地代理;這兩個類都繼承自 IBinder, 因而都具有跨進程傳輸的能力;實際上,在跨越進程的時候,Binder 驅動會自動完成這兩個對象的轉換。在使用 AIDL 的時候,編譯工具會給我們生成一個 Stub 的靜態內部類;這個類繼承了 Binder, 說明它是一個 Binder 本地對象,它實現了 IInterface 接口,表明它具有遠程 Server 承諾給 Client 的能力;Stub 是一個抽象類,具體的 IInterface 的相關實現需要我們手動完成,這里使用了策略模式。它總是那么一種固定的模式:一個需要跨進程傳遞的對象一定繼承自 IBinder,如果是 Binder 本地對象,那么一定繼承 Binder 實現 IInterface,如果是代理對象,那么就實現了 IInterface 并持有了 IBinder 引用;
Proxy 與 Stub 不一樣,雖然他們都既是 Binder 又是 IInterface,不同的是 Stub 采用的是繼承(is 關系),Proxy 采用的是組合(has 關系)。
Binder 通信模型有四個角色(Client / Server / ServiceManager / Driver),通信步驟大致如下:
有一個進程向驅動申請為 ServiceManager,驅動同意之后,它便可以負責對 Service 的管理;Server 向 ServiceManager 注冊,每一個 Service 端的進程啟動之后要向 ServiceManager 報告,ServiceManager 建立一張表,對應著各個 Server 的名字和地址;Client 詢問 ServiceManager 如何聯系到想要聯系的 Server,ServiceManager 返回一個消息,Client 根據這個消息就可以開始建立與 Server 的通信; 這里 Client 與 ServiceManager 的通信,Client 與 Server 的通信都會經過驅動,驅動是整個過程的核心;兩個運行在用戶空間的進程 A 和進程 B 如何完成通信?有一種方案: 內核可以訪問 A 和 B 的所有數據;所以,可以通過內核做中轉;假設進程 A 要給進程 B 發送數據,那么就先把 A 的數據 copy 到內核空間,然后把內核空間對應的數據 copy 到 B 就完成了;用戶空間要操作內核空間,需要通過系統調用;剛好,這里就有兩個系統調用:copy_from_user, copy_to_user。 但是但是但是,Binder 機制并不是這么做的!!!看圖(該圖來自Binder 學習指南)

由于驅動返回的 objectProxy 與 Server 里的原始的 object 近乎一樣,給人感覺是直接 “ 把 Server 進程里面的對象 Object 傳遞給了 Client 進程 ”,我們因此說是 “ Binder 對象是可以進行跨進程傳遞的一種對象 ”。 其實不然,Binder 跨進程傳輸并不是真的把一個對象傳輸給了另一個進程;傳輸過程好像是 Binder 跨進程穿越的時候玩了一個魔術,它在一個進程留下了一個真身,在另一個進程幻化出一個影子(這個影子可以有多個);所以說!!! Client 進程的操作實際上是對于影子的操作,影子利用 Binder 驅動最終讓真身完成操作。 Android 系統實現這種機制使用的是 代理模式, 對于 Binder 的訪問,如果是在同一個進程(不需要跨進程),那么直接返回原始的 Binder 實體;如果在不同進程,那么就給他一個代理對象(影子);我們在系統源碼以及 AIDL 的生成代碼里面可以看到很多這種實現。
PS:Server 進程向 ServiceManager 注冊的過程也是跨進程通信,驅動也會對這個過程進行暗箱操作:ServiceManager 中存在的 Server 端的對象實際上也是代理對象,后面 Client 向 ServiceManager 查詢的時候,驅動會給 Client 返回另外一個代理對象。Server 進程的本地對象僅有一個,其他進程所擁有的全部都是它的代理。
一句話總結就是:Client 進程只不過是持有了 Server 端的代理;代理對象協助驅動完成了跨進程通信。
End.
Note by HF. Learn from Internet & 《Android 開發藝術探索》
新聞熱點
疑難解答