有些東西你一直在用它,但卻一直不知道自己在使用它。在Android的應用開發中,Binder就是這么一個角色,你編寫的應用都使用過Binder機制(如startActivity的執行過程),但是你卻說不出什么是Binder,甚至很多人都不知道Binder的存在。
那么,這種已經融入你的使用但又不讓人發覺的機制對我們做應用開發有用嗎?很多人認為并沒有用,往往在他們的印象里Binder是和驅動一起出現的,叫“Binder驅動”,而我們做應用層開發的基本上不需要去修改或者開發驅動。這樣一來,Binder對于應用開發者來說就顯得很神秘但是和自己無關。
面試題:Binder是什么?它是如何實現跨進程通信的?
Binder的英文原意是“膠水”的意思,其實很形像了。Binder模糊了進程邊界,淡化了進程間通信的過程,整個系統仿佛運行于同一個面向對象的程序之中。形形色色的Binder對象以及星羅棋布的引用仿佛粘接各個應用程序的膠水。
要理解Binder當然要先從Linux進程說起。
Linux進程基礎
為了保護進程空間不被別的進程破壞或者干擾,Linux的進程是相互獨立的(進程隔離),而且一個進程空間還分為用戶空間和內核(Kernel)空間,相當于把Kernel和上層的應用程序抽像的隔離開。這里有兩個隔離,一個進程間是相互隔離的,二是進程內有用戶和內核的隔離。
即然有隔離,那么它們之前要相互配合時就得有合作(交互)。進程間的交互就叫進程間通信(IPC,或稱跨進程通信),而進程內的用戶和內核的交互就是系統調用。
用戶空間訪問內核空間的唯一方式就是系統調用;通過這個統一入口接口,所有的資源訪問都是在內核的控制下執行,以免導致對用戶程序對系統資源的越權訪問,從而保障了系統的安全和穩定。
也就是說,為了保證安全性和獨立性,一個進程是不能直接操作或者訪問別一個進程空間的。Android即然是架設在Linux基礎之上的,當然也要解決這個進程間通信的問題。
為什么要使用Binder?
在傳統的Linux上,我們還是有很多選擇可以用來實現進程間通信,如管道、SystemV、Socket等。那么Android為什么不使用這些原有的技術,而是要使開發一種新的叫Binder的進程間通信機制呢?
主要有兩個方面的原因:
性能方面
在移動設備上(性能受限制的設備,比如要省電),廣泛地使用跨進程通信對通信機制的性能有嚴格的要求,Binder相對出傳統的Socket方式,更加高效。Binder數據拷貝只需要一次,而管道、消息隊列、Socket都需要2次,共享內存方式一次內存拷貝都不需要,但實現方式又比較復雜。安全方面
傳統的進程通信方式對于通信雙方的身份并沒有做出嚴格的驗證,比如Socket通信ip地址是客戶端手動填入,很容易進行偽造,而Binder機制從協議本身就支持對通信雙方做身份校檢,因而大大提升了安全性。
還有一些好處,如實現面象對象的調用方式,在使用Binder時就和調用一個本地實例一樣。
Binder運行機制
Binder基于Client-Server通信模式,除了Client端和Server端,還有兩角色一起合作完成進程間通信功能。
Binder通信的四個角色:
- Client進程:使用服務的進程。
- Server進程:提供服務的進程。
- ServiceManager進程:ServiceManager的作用是將字符形式的Binder名字轉化成Client中對該Binder的引用,使得Client能夠通過Binder名字獲得對Server中Binder實體的引用。
- Binder驅動:驅動負責進程之間Binder通信的建立,Binder在進程之間的傳遞,Binder引用計數管理,數據包在進程之間的傳遞和交互等一系列底層支持。
初次接觸這些概念可能會覺得難于理解,讀者可以把四個角色和熟悉的互聯網進行類比:Server是服務器,Client是客戶終端,ServiceManager是域名服務器(DNS),驅動是路由器。這樣類比,你很容易就能理解下圖:
Binder的運行機制就很好理解了,Server進程向Service Manager進程注冊服務(可訪問的方法接口),Client進程通過Binder驅動可以訪問到Server進程提供的服務。Binder驅動管理著Binder之間的數據傳遞,這個數據的具體格式由Binder協議定義(可以類比為網絡傳輸的TCP協議)。并且Binder驅動持有每個Server在內核中的Binder實體,并給Client進程提供Binder的引用。
Binder跨進程傳輸并不是真的把一個對象傳輸到了另外一個進程;傳輸過程好像是Binder跨進程穿越的時候,它在一個進程留下了一個真身,在另外一個進程幻化出一個影子(這個影子可以很多個);Client進程的操作其實是對于影子的操作,影子利用Binder驅動最終讓真身完成操作。
Binder的線程管理
每個Binder的Server進程會創建很多線程來處理Binder請求,可以簡單的理解為創建了一個Binder的線程池吧(雖然實際上并不完全是這樣簡單的線程管理方式),而真正管理這些線程并不是由這個Server端來管理的,而是由Binder驅動進行管理的。
一個進程的Binder線程數默認最大是16,超過的請求會被阻塞等待空閑的Binder線程。理解這一點的話,你做進程間通信時處理并發問題就會有一個底,比如使用ContentProvider時(又一個使用Binder機制的組件),你就很清楚它的CRUD(創建、檢索、更新和刪除)方法只能同時有16個線程在跑。
Binder對應用開發者的用處
上面提到的線程數是一個對應用開發有用的地方,很多人之前由于不了解有這個線程數的限制可能會在這里遇到過麻煩。
當然,最重要的是你了解了Android的進程間通信機制,知道Android的組件間是如何實現進程間通信和數據共享的,當你的應用要處理進程間通信時,你知道可能要關注哪個方面。如你的Activity組件可以設置在不同的進程中運行,那么每個進程都是獨立的,它要使用別的進程的數據時,你就會知道用靜態變量去交互沒有意義。而且,理解了原理,再去編寫AIDL的代碼就沒有那么難理解了。
對于一些Framework層的開發者,如自已定制ROM的團隊,需要提供自己的系統服務時,Binder機制肯定是必需要了解的,不然無從下手。
小結
多進程問題不是每個開發都會遇到的,很多應用只要在自己的進程內完成業務需求就可以了,但知道多進程的存在,并理解Android提供的Binder機制,那么我們在選擇如何做進程間的互交時就會更明確可能存在的問題和Android的解決方案。
也許,這幾段講解后,你還是一知半解的,那么請看下一篇和AIDL相關的實戰。