淺析Android Binder機制

org_20170307100200_818.jpg

在Android開發(fā)過程中,Binder的身影無處無處不在,我們編寫的程序都使用過Binder機制(例如startActivity的執(zhí)行過程)但是請問你知道什么是Binder么?在開發(fā)過程中你察覺到它的存在了么?

什么是Binder

Biner翻譯過來是“膠水“的意思,這個翻譯分形象。Binder的主要工作就是淡化了進(jìn)程邊界,淡化了進(jìn)程間通信的過程。要是想更好的理解Binder就必須從Linux進(jìn)程談起。

Linux進(jìn)程

為了保護(hù)進(jìn)程空間不被別的進(jìn)程破壞或者干擾,Linux中的進(jìn)程是相互獨立的,也就是所謂的進(jìn)程隔離。(而且一個進(jìn)程的內(nèi)存空間還被分為了用戶空間和內(nèi)核空間,二者也是相互隔離的。這里不做探討)所以在Linux中,進(jìn)程與進(jìn)程之間是相互隔離的,而且進(jìn)程中的用戶和內(nèi)核空間也是隔離的。

也就是說為了安全和獨立,一個進(jìn)程是不能直接操作或者訪問另外一個進(jìn)程的內(nèi)存空間的。他們之間既然是隔離的,在需要通信、協(xié)作的時候就需要使用進(jìn)程間通信技術(shù)(即IPC,也稱跨進(jìn)程通信),我們都知道Android框架是建立在Linux之上的,當(dāng)然也會面對進(jìn)程間通訊的問題。

為什么使用Binder

在Linux系統(tǒng)中為了達(dá)到進(jìn)程間通訊的目的,我們可以選用諸如管道、Socket等技術(shù)手段,那么為什么Android選用了Binder這種新型的IPC技術(shù),放棄了原有成熟的技術(shù)呢?

主要有如下兩個方面:

  1. 性能角度

由于在移動設(shè)備諸如省電等性能的考慮,廣泛地使用進(jìn)程間通訊對于通信機制的性能有嚴(yán)格的要求,Binder相對于傳統(tǒng)的Socket、管道方式更加高效。Bidner數(shù)據(jù)拷貝只需要一次,而管道、消息隊列、Socket都需要2次,內(nèi)存共享方式一次內(nèi)存拷貝都不需要,但是實現(xiàn)起來難度高,復(fù)雜性大。

2.穩(wěn)定性角度

傳統(tǒng)的進(jìn)程通信方式對于通信雙方的身份并沒有做出嚴(yán)格的驗證,比如Socket通信ip地址是客戶端手動填入,很容易進(jìn)行偽造,而Binder機制從協(xié)議本身就支持對通信雙方做身份校檢,因而大大提升了安全性。

Binder機制

IPC機制簡單描述

這里寫圖片描述

每個Android的進(jìn)程,只能運行在自己進(jìn)程所擁有的虛擬地址空間。對應(yīng)一個4GB的虛擬地址空間,其中3GB是用戶空間,1GB是內(nèi)核空間,當(dāng)然內(nèi)核空間的大小是可以通過參數(shù)配置調(diào)整的。對于用戶空間,不同進(jìn)程之間彼此是不能共享的,而內(nèi)核空間卻是可共享的。Client進(jìn)程向Server進(jìn)程通信,恰恰是利用進(jìn)程間可共享的內(nèi)核內(nèi)存空間來完成底層通信工作的,Client端與Server端進(jìn)程往往采用ioctl等方法跟內(nèi)核空間的驅(qū)動進(jìn)行交互。

舉例分析

首先我們看看我們的程序跨進(jìn)程調(diào)用系統(tǒng)服務(wù)的簡單示例,實現(xiàn)浮動窗口部分代碼:

//獲取WindowManager服務(wù)引用
WindowManager wm = (WindowManager)getSystemService(getApplication().WINDOW_SERVICE);  
//布局參數(shù)layoutParams相關(guān)設(shè)置略...
View view=LayoutInflater.from(getApplication()).inflate(R.layout.float_layout, null);  
//添加view
wm.addView(view, layoutParams);

系統(tǒng)服務(wù)都是運行在systemServer進(jìn)程中,因此我們調(diào)用系統(tǒng)服務(wù)都是跨進(jìn)程的調(diào)用。第2行代碼中,得到的wm是WindowManager對象的引用,第6行調(diào)用WindowManager的addView函數(shù),將觸發(fā)遠(yuǎn)程調(diào)用,調(diào)用的是運行在systemServer進(jìn)程中的WindowManager的addView函數(shù)。那么addView函數(shù)做了什么呢?我們繼續(xù)看。

代碼執(zhí)行過程

假設(shè)你已經(jīng)創(chuàng)建好服務(wù)端類MyService、客戶端類MyClient。在客戶端持有MyService的引用,并且調(diào)用了MyService的func函數(shù),那么Android內(nèi)部調(diào)用過程如下:

這里寫圖片描述

代碼調(diào)用過程

看了這個圖以后,相信你對你的代碼在調(diào)用遠(yuǎn)程進(jìn)程函數(shù)時有個全局的認(rèn)識。這張圖有一點很重要,就是客戶端當(dāng)前線程會被掛起!因此,如果遠(yuǎn)程進(jìn)程是執(zhí)行長時間的運算,請不要使用主線程去調(diào)用遠(yuǎn)程函數(shù),以防止ANR。

Binder的C/S架構(gòu)

上面一節(jié)我們對遠(yuǎn)程進(jìn)程調(diào)用代碼執(zhí)行過程有個初步了解,在Android開發(fā)中,我們大量使用到了系統(tǒng)Service,比如媒體播放、各種傳感器以及WindowManagerService等等等等(太多了~)。那么Android是怎么管理這些服務(wù),并且讓用戶跨進(jìn)程調(diào)用這些服務(wù)呢?首先我們看看調(diào)用系統(tǒng)服務(wù)的過程。在Android開機啟動過程中,Android會初始化系統(tǒng)的各種Service,并將這些Service向ServiceManager注冊(即讓ServiceManager管理)??蛻舳讼胍玫骄唧w的Service直接向ServiceManager要即可。客戶端首先向ServiceManager查詢得到具體的Service引用,然后通過這個引用向具體的服務(wù)端發(fā)送請求,服務(wù)端執(zhí)行完成后就返回。

這里寫圖片描述

Binder驅(qū)動實現(xiàn)原理

服務(wù)端跨進(jìn)程的類都要繼承Binder類。我們所持有的Binder引用(即服務(wù)端的類引用)并不是實際真實的遠(yuǎn)程Binder對象,我們的引用在Binder驅(qū)動里還要做一次映射。也就是說,設(shè)備驅(qū)動根據(jù)我們的引用對象找到對應(yīng)的遠(yuǎn)程進(jìn)程??蛻舳艘{(diào)用遠(yuǎn)程對象函數(shù)時,只需把數(shù)據(jù)寫入到Parcel,在調(diào)用所持有的Binder引用的transact()函數(shù),transact函數(shù)執(zhí)行過程中會把參數(shù)、標(biāo)識符(標(biāo)記遠(yuǎn)程對象及其函數(shù))等數(shù)據(jù)放入到Client的共享內(nèi)存,Binder驅(qū)動從Client的共享內(nèi)存中讀取數(shù)據(jù),根據(jù)這些數(shù)據(jù)找到對應(yīng)的遠(yuǎn)程進(jìn)程的共享內(nèi)存,把數(shù)據(jù)拷貝到遠(yuǎn)程進(jìn)程的共享內(nèi)存中,并通知遠(yuǎn)程進(jìn)程執(zhí)行onTransact()函數(shù),這個函數(shù)也是屬于Binder類。遠(yuǎn)程進(jìn)程Binder對象執(zhí)行完成后,將得到的寫入自己的共享內(nèi)存中,Binder驅(qū)動再將遠(yuǎn)程進(jìn)程的共享內(nèi)存數(shù)據(jù)拷貝到客戶端的共享內(nèi)存,并喚醒客戶端線程。

Binder機制運用

好了,現(xiàn)在對Binder機制已經(jīng)理解了,我們再看看Android是怎么運用Binder的。再現(xiàn)前面代碼:

//獲取WindowManager服務(wù)引用
WindowManager wm = (WindowManager)getSystemService(getApplication().WINDOW_SERVICE);  
//布局參數(shù)layoutParams相關(guān)設(shè)置略...
View view=LayoutInflater.from(getApplication()).inflate(R.layout.float_layout, null);  
//添加view
wm.addView(view, layoutParams);

這段代碼前面已經(jīng)出現(xiàn)過。getSystemService(getApplication().WINDOW_SERVICE);函數(shù)內(nèi)部原理就是向ServiceManager查詢標(biāo)識符為getApplication().WINDOW_SERVICE的遠(yuǎn)程對象的引用。即WindowManager對象的引用,這個引用的真正實現(xiàn)是WindowManager的某個代理。得到這個引用后,在調(diào)用addView時,真正的實現(xiàn)是在代理里面,代理把參數(shù)打包到Parcel對象中,然后調(diào)用transact函數(shù)(該函數(shù)繼承自Binder),再觸發(fā)Binder驅(qū)動的一系列調(diào)用過程。

「參考文章」
Android面試一天一題(Day 35:神秘的Binder機制)
簡單明了,徹底地理解Binder

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容