Android Binder機制介紹,androidbinder機制
做過Android開發的同學可能有些體會,入門初期,工作內容主要是實現各式各樣的UI介面,以及實現應用的商務邏輯。在這個階段,我們會逐漸熟悉View系統,逐漸學會實現各種各樣的介面以及動畫效果。再往後,當我們想更深入的學習android系統,比如學習android四大組件的啟動過程、AMS、PMS等等時,都會遇到一個叫做Binder的東西。結合筆者的經驗,Binder可以說是深入理解Android系統的重要基礎。binder作為android系統處理序間通訊的機制,貫穿在方方面面。我們平時使用最多的startActivity、startService都是通過binder機制與AMS所在進程進行通訊。本文主要對Binder機制的體繫結構作簡要介紹,相信讀者看完後,會對binder有一個總體上的理解與把握。
註:筆者初學binder時,曾經看過一些binder介紹的文章,並且過早的糾結於一些文章中的binder代碼細節,感覺非常吃力。本文僅從宏觀上對binder機制進行介紹。相信讀者先理解了binder的總體結構後,再去深入細節,學習效果會更好。
Binder是什麼、能做什嗎?
Binder是android系統裡面的處理序間通訊機制。androd系統中,不同的app運行在不同的進程中,同一個app的不同組件也可能運行在不同的進程中(androidManifest檔案中android:process)。當一個進程想為其它進程提供服務時,就需要通過處理序間通訊的方式來提供服務。打個比方:我們有一個APP1,裡面有個service組件可以提供計算機的服務。當另外一個APP2也想使用APP1裡面的service的計算機的服務時,由於不同的APP運行在不同的進程中,所以,APP2是無法直接使用APP1裡面的service。由於跨越了進程,只能通過處理序間通訊機制來完成。
再說的形象點,APP1所在進程有一個對象object1,其中有一個方法method1。 APP2所在的另外一個進程,想使用object1的method1方法。binder可能協助我們在APP2所在進程拿到一個object1對象的引用,使我們能夠像調用本機物件一樣,通過object1.method1()直接調用。利用binder,我們可以突破進程的限制,將對象傳給其它進程,讓其它進程方便調用對象的方法。
為什麼用Binder?
理解了binder是什麼、能做什麼後,大家可能會有疑問:android系統基於Linux,linux本身具有很多的處理序間通訊方式可供選擇,為什麼android不使用linux內建的一個處理序間通訊方式,而新創造了一個binder?是重複造輪子嗎?
Linux內建的處理序間通訊方式有:檔案、signal、socket、Pipe、共用記憶體..., 為什麼使用Binder?筆者總結原因有兩大方面:
1 曆史原因。Binder最早並不是為Android系統而設計的,最開始有一個OpenBinder的東西,用在一個叫做Palm Cobaltw的為核心作業系統上。後來,Palm Cobaltw移植到了Linux系統上,OpenBinder也跟著移植了過來。Google在組建AndroidTeam Dev的時候,聘請了一位叫做Dianne Hackborn的工程師,而他就是OpenBinder的核心人員。後面在做android處理序間通訊時,發現binder很合適,就理所當然的在android系統上使用了Binder。
2 binder自身的一些特點和優勢。Binder實現處理序間通訊時,在安全性和效率方面,都很合適用在android系統中。關於這點,先有個印象即可。
Binder有哪些組成部分?
一個Binder系統由四部分組成:Binder用戶端、Binder服務端、Binder驅動、服務登記查詢模組
Binder用戶端:想要使用服務的進程
Binder服務端:實際提供服務的進程
Binder驅動:我們在用戶端先通過Binder拿到一個服務端進程中的一個對象的引用,通過這個引用,直接調用對象的方法擷取結果。在這個引用對象執行方法時,它是先將方法調用的請求傳給binder驅動;然後binder驅動再將請求傳給服務端進程;服務端進程收到請求後,調用服務端“真正”的對象來執行所調用的方法;得出結果後,將結果發給binder驅動;binder驅動再將結果發給我們的用戶端;最終,我們在用戶端進程的調用就有了傳回值。Binder驅動,相當於一個中轉者的角色。通過這個中轉者的幫忙,我們就可以調用其它進程中的對象。
服務登記查詢模組:我們調用其它進程裡面的對象時,首先要擷取這個對象。這個對象其實代表了另外一個進程能給我們提供什麼樣的服務(再直接一點,就是:對象中有哪些方法可以讓用戶端進程調用)。首先服務端進程要在某個地方註冊登記一下,告訴系統我有個對象可以公開給其它進程來提供服務。當用戶端進程需要這個服務時,就去這個登記的地方通過查詢來找到這個對象。
Binder各部分是如何工作的?
下面從用戶端進程的角度來看看binder的工作機制
作為用戶端進程,僅僅是想使用服務端進程提供的服務而已: 但是由於進程的隔離性,Client所在的ProcessA是不能讀寫Service所在的ProcessB中的內容,但系統核心可以,而Binder驅動就是運行在核心態。Binder驅動幫我們中轉請求即可: 有了Binder驅動後,對於Client端和Service端,需要額外專門與Binder驅動打交道,為了幫Client和Service端屏蔽掉與Binder驅動打交道這件很low的工作,我們可以分別為Client和Service設計一個代理,讓他們只與代理打交道即可,將與Binder驅動打交道的任務放在代理裡面:讀者可能要問,如果要自己實現Client端和Service端,通過上面的方式,雖然邏輯上獨立出來了兩個代理(用戶端代理的Proxy、服務端的代理Stub),這兩個代理還不是要自己實現?如果還是要自己實現,那分出來又有實際的意義?AndroidSDK為我們提供了一個AIDL工具。我們只要建立一個AIDL檔案,像建立普通interface一樣(略有不同)在裡面定義介面方法。定義好之後,androidSDK會根據aidl裡面的介面定義,自動為我們產生一個java檔案,其中就包括用戶端代理Proxy和服務端代理Stub,並且自動產生了與Binder驅動通訊的代碼,是不是很爽。上面的圖片中,之所以將用戶端代理叫做Proxy,服務端代理叫做Stub,就是因為aidl自動產生的代理中,兩個代理的類名就是Proxy和Stub。我們在做的實際工作就只有真正的服務功能邏輯了。 再更近一步的想想,對於用戶端而言,更理想的狀態是,用戶端完全不用知道調用的對象是一個本機物件,還是一個服務端進程中的對象。按照上面的圖片,用戶端顯然知道自己調用的是遠程對象,所以通過一個Proxy來調。我們在開發android應用程式的過程中,都會用到一些系統提供的XXXManager(ActivityManager、PackageManager、WifiManager、PowerManager等等),而這些manager所要實現的目的之一,正是幫Client屏蔽掉Binder的實現細節。有了這些Manager,Client在使用時,首先通過getServiceManager(xxx)擷取一個manager對象,後面就直接調用manager中封裝好的服務方法即可。這樣以來,manager內部實際上還是通過用戶端代理,通過binder驅動來跟運行在另外一個進程中的xxxService來通訊,但對於Client來說,完全不用知道。如:對於一些系統服務,ActivityManager、PackagerManager等等,不光為Client螢幕了Binder相關細節,還可以對真正的服務端對象提供的服務API進行過濾和控制,可以只為Client暴露一個服務API的子集。 最後一個疑問:到上面的圖片所講的內容為止,前提條件是Client先擷取到了一個Proxy或者Manager,然後才完成後面的處理序間通訊。那到底Client是如何擷取到Manager或者Proxy呢?首先以一些系統服務為例:前面提到,Binder機制的四個組成部分中,有一個是“服務登記查詢模組”,對應中的Context Manager,在Android系統中運行時的進程名為:servicemanager。在所有服務進程中,servicemanager第一個啟動,原因也不難理解,如果其它服務進程先於servicemanager啟動,到哪裡去註冊? 如所示,假設Service所在的進程ProcessB在servicemanager進程啟動後啟動,ProcessB需要向servicemanager註冊。“註冊”這個動作,本質也是要跨進程通訊,從ProcessB到servicemanager,這時,Service成為了本次跨進程通訊的用戶端,ContextManager成為了服務端。用戶端到服務端的通訊,按前面講到的內容,需要一個Proxy或者Manager。這個Proxy或者Manager從哪裡擷取呢?問題好像陷入了迴圈死結?因為ContextManager的特殊地位,讀者可以理解為系統對它有點特殊待遇,Service可以從一個全域固定的地方直接去拿Proxy或者Manager,而不用像Client那樣通過查詢來擷取Proxy/Manager。拿到了用戶端代理,具體註冊的通訊過程如上文所講,不再重複。到這裡為止,Service就成功註冊到了ContextManager中。 用戶端Client初始化Manager時,manager也像剛才的Service,直接從某個固定的地方拿到ContextManager的代理,通過代理,去查詢ContextManager,擷取一個“提供所要服務的代理對象”。manager有了這個對象,Client就可以通過manager來使用服務了,調用邏輯 當自己來實現Client和Service時,Client如何擷取Proxy代理對象?先講一下實現方式:一般我們會在App中通過Service組件的方式來建立一個服務。其它App通過BindService的方式串連到Service,通過onServiceConnected回調就能擷取Proxy代理對象。具體實現可參考:http://blog.csdn.net/singwhatiwanna/article/details/17041691
總結
本文簡要介紹了Binder跨進程通訊機制的邏輯,希望能協助初學Binder的同學快速入門,提交學習效率。文中使用的圖片以及講解思路,來自於這個文檔:
http://events.linuxfoundation.org/images/stories/slides/abs2013_gargentas.pdf,加進去一些筆者自己的思考和總結。一些地方如果有錯誤,歡迎指正交流。
另外推薦一篇binder入門的文章:http://weishu.me/2016/01/12/binder-index-for-newer/