1、常見通訊方式梳理
進程間常見的通訊方式有如下幾種:
一、Socket:通用接口,傳輸效率低,主要用在跨網絡通訊和本機進程間通訊,傳輸過程須要拷貝2次數據;
二、共享內存:雖然無需拷貝,但控制複雜;
三、Binder:基於C/S模式,只需1次拷貝,安全性高。java
不一樣的通訊方式使用場景也不一樣:android
2、 Binder和AIDL
一、Binder機制淺析git
Binder機制,總體上基於C/S架構實現,客戶端先獲取遠程服務端的對象引用,經過該引用調用遠程函數,而後由Binder驅動找到對應的服務端函數再執行。其設計初衷是,下降使用複雜度、改善安全性、並提升傳輸效率。github
其中的Binder驅動,是做爲一個特殊字符型設備存在,設備節點爲/dev/binder,遵循Linux設備驅動模型。在驅動實現過程當中,系統主要經過 binder_ioctl 函數與用戶空間的進程交換數據。binder_thread_write函數用於發送請求或返回結果,而binder_thread_read函數用於讀取結果。安全
二、AIDL
AIDL本質是系統提供的一套可快速實現Binder調用的工具/語言,其中工具也就是aidl.exe,可將aidl文件轉爲java文件,語言就是aidl文件使用的接口描述語言,用於.aidl文件。性能優化
1)關鍵類和方法網絡
補充,transact調用中的code參數,通常對應具體的功能,相似index,一個Binder對象使用的code值,在應用內部必須可以區分,但不一樣的Binder對象使用的code值能夠重複。架構
在服務端進程中,有個Stub類,這個是Binder的實現類,服務端經過這個類來提供服務。注意該類是抽象類,其static方法asInterface給客戶端調用,其餘方法給系統調用,最終會調用到具體實現(通常在Stub的實例對象中), 其onTransact()方法運行在服務端的Binder線程池中,當客戶端發起transact跨進程請求時,系統回調此方法處理請求)。併發
在客戶端進程中,asInterface()是AIDL接口中的一個方法,提供客戶端調用。該方法能夠將服務端的返回的Binder對象或BinderProxy對象,轉換成客戶端所須要的AIDL接口類型對象(一個Stub.Proxy實例)。函數
2)代理類的設計
BinderProxy是Android系統用於跨進程通訊、訪問遠程服務中的Binder實例對象而設計的,側重系統調用,對用戶不透明。實際上它是在native層javaObjectForIBinder方法中建立的,屬於客戶端進程中的對象,和Native層的BpBinder有對應關係。
形如IxxxService.Stub.Proxy這種代理類,用於跨進程通訊、訪問遠程服務中的用戶自定義方法而設計的,側重應用層,實際上該類基本都是經過其實現的AIDL接口例如IActivityManager來調用。
對於Service組件的遠程綁定,BinderProxy與Binder實例(好比IxxxService.Stub類型的變量mBinder)的映射關係,是經過BinderProxy.ProxyMap這個類維護的。而IxxxService.Stub.Proxy實例與Binder實例(IxxxService.Stub類型的變量mBinder)的映射關係,是經過Proxy類的構造函數及其成員變量mRemote維護的,這裏mRemote就是mBinder在客戶端進程的代理也就是BinderProxy對象。
若是咱們獲取到Proxy類的實例,能夠調用其asBinder方法獲取該實例維護的BinderProxy對象,反過來也能夠經過BinderProxy實例的asInterface方法來獲取Proxy對象。
3、 融合與思考
一、Binder常見使用場景解析
場景1:綁定服務,例如WallpaperService,其代理對象所需的BinderProxy實例(遠程服務時),是onServiceConnected回調參數中IBinder類型的service。
場景2:系統服務,例如AMS、WMS等,其代理對象所需的BinderProxy實例,是經過ServiceManager.getService方法調用返回的對象。
場景3:間接訪問核心組件,例如IWindowSession。IWindowSession的實現類是Session,不屬於服務。客戶端例如WindowManagerGlobal,先經過ServiceManager獲取WMS的本地代理也就是IWindowManager的實例,再利用此代理的openSession方法獲得Session的本地代理IWindowSession。
Binder跨進程通訊的關鍵,是一個實現了IBinder接口的對象,該對象可用於調用遠程服務的本地Binder類的asInterface方法,得到遠程類的本地代理。如前所述,得到IBinder對象的途徑有3種以上,包括利用ServiceConnection回調(綁定服務)直接得到;或者利用ServiceManager(系統服務)直接得到;或者經過別的代理(基於系統服務或綁定服務)對象的某些方法間接得到。
除了上面3種場景,還有一種特殊狀況:IServiceManger。該類派生於android.os.IInterface,定義了用於實現具體功能的接口,例如addService、getService。
ServiceManagerNative派生於Binder,做爲遠程ServiceManager的本地Binder類,提供asInterface方法(對應工具自動生成的java類中的IxxService.Stub)。ServiceManagerProxy是遠程ServiceManager的本地代理類,提供了各項具體功能方法,利用Parcel、IBinder和遠程ServiceManager通訊並返回結果(對應於工具生成的IxxService.Stub.Proxy)。
獲取本地代理,首先須要獲得一個IBinder接口實例,這裏主要是經過BinderInternal.getContextObject()完成的,屬於系統級的一個特殊處理。其實是native層建立的。遠程ServiceManager,也就是addService等功能具體的實現,最終是service_manager.c中完成。
這裏能夠看出,Binder通訊的靈活性,不必定須要aidl文件,遠端服務也不必定要包含一個Stub對象或者派生於Stub類,能夠直接在native層。
AIDL機制既能夠實現跨進程通訊,也支持在同一進程內的調用;在同一進程時,onServiceConnected回調的入參Binder對象,就是服務端返回的Binder實例,無需轉換(對於進程的判斷,以及Binder對象的對應區分,例如是Binder仍是BinderProxy,是在驅動裏面判斷的,詳見binder.c中的binder_translate_handle)。
二、Binder和Parcelable接口
Binder進程通訊機制幫咱們解決的最大問題,就是可以經過一個代理來訪問其餘進程中的實例對象及其方法,其中的數據傳輸,大量使用了Parcel以及Parcelable接口。
若是是跨進程傳輸自定義數據類(可序列化的),可使用實現了Parcelable接口的自定義數據類,一樣須要對應的aidl文件。若是一個類在建立實例時,必須調用沒法序列化的組件,則須要給客戶端提供代理才能訪問該類,也就是說,服務端的實現必須派生於Ixxx.Stub(Binder的派生類),使得客戶端經過某種方式能夠獲取其代理。
對於功能簡單的調用,例如數據訪問相關功能,使用一個實現了Parcelable接口的類基本就能夠了,最可能是引用其餘aidl接口。例如KeyEvent,用於序列化的構造函數KeyEvent(Parcel in)裏面都是能夠能夠序列化的數據類型。但若是自定義類的功能複雜,依賴大量組件,重點是構造函數參數涉及到不屬於aidl能夠序列化的類時,則沒法實現Parcelable接口,只能給調用者/客戶端提供一個代理來訪問這些功能,這時候就須要定義aidl,而且讓功能實現類派生於Stub(實質上是派生於Binder類)。例如Session類,其構造函數的幾個參數都不能序列化,這個是其沒有實現Parcelable接口卻卻實現了IWindowSession.Stub的關鍵緣由。
(相關完整且成體系的文章,可參見本人原創的開源電子書《Android系統與性能優化》,地址:https://github.com/carylake/androidnotes)