帶你簡單理解Binder

前言

我們看過很多關(guān)于Binder的文章椅您,但是看完大多數(shù)文章后,都會有不知所云的感覺生闲,是因?yàn)槟切┪恼虏粔蚝脝嵯蹦纾坎皇悄切┪恼轮v得不夠好,我們看的不明白主要是存在兩種情況碍讯,一種深入代碼細(xì)節(jié)不能自拔悬蔽,從FrameWork到Kernel層,長篇累牘捉兴,讓人很難理解Binder蝎困;另一種是只講framework層,Binder驅(qū)動并沒有具體提到倍啥,導(dǎo)致我們會用Binder禾乘,也大致能說的出一些原理,可并沒有一個完整的深刻認(rèn)知虽缕。所以我不打算深入討論Binder的底層細(xì)節(jié)始藕,也不會貼出一大堆代碼,帶著大家一起看代碼彼宠,因?yàn)橛泻芏鄷臀恼略谶@方面做的已經(jīng)很細(xì)致了鳄虱,這篇文章是從自己的角度,解讀一下Binder機(jī)制凭峡,給大家一個簡單清晰的解釋拙已。

為什么使用Binder

Android使用的Linux內(nèi)核擁有著非常多的跨進(jìn)程通信機(jī)制,比如管道摧冀,System V(消息隊(duì)列倍踪、信號量和共享內(nèi)存),Socket等索昂;為什么還需要單獨(dú)搞一個Binder出來呢建车?主要有兩點(diǎn):

  • 性能
  • 安全

在移動設(shè)備上,廣泛的使用跨進(jìn)程通信肯定對通信機(jī)制本身提出了嚴(yán)格的要求椒惨;Binder相對傳統(tǒng)的Socket方式缤至,更加高效;另外康谆,傳統(tǒng)的進(jìn)程通信方式對于通信雙方的身份并沒有做出嚴(yán)格的驗(yàn)證领斥,只有在上層協(xié)議上進(jìn)行架設(shè)嫉到;碧柔Socket通信ip地址是客戶端手動填寫的,都可以進(jìn)行偽造月洛;而Binder機(jī)制從協(xié)議本身就支持對通信雙方做身份校驗(yàn)何恶,因而大大提升了安全性。這也是Android權(quán)限模型的基礎(chǔ)嚼黔。

什么是Binder

《Android開發(fā)藝術(shù)探索》一書中對Binder做了詳細(xì)的解釋:

Binder是Android中的一個類细层,它實(shí)現(xiàn)了IBinder接口。從IPC(Inter-Process Communication)角度來說唬涧,Binder是Android中一種跨進(jìn)程通信方法疫赎,Binder還可以理解為一種虛擬的物理設(shè)備,它的設(shè)備驅(qū)動是/dev/binder爵卒,該通信方式在Linux中沒有虚缎;從AndroidFramework角度來說,Binder是ServiceManager連接各種Manager(ActivityManager钓株、WindowManager实牡,等等)和相應(yīng)ManagerService的橋梁;從Android應(yīng)用層來說轴合,Binder是客戶端和服務(wù)端進(jìn)行通信的媒介创坞,當(dāng)bindService的時候,服務(wù)端會返回一個包含了服務(wù)端業(yè)務(wù)調(diào)用的Binder對象受葛,通過這個Binder對象题涨,客戶端可以獲取服務(wù)端提供的服務(wù)或者數(shù)據(jù),這里的服務(wù)包括普通服務(wù)和基于AIDL的服務(wù)总滩。

上面的介紹纲堵,很多人看過之后,如果不理解闰渔,可能看過之后就會忘掉席函,我們暫時可以把Binder理解為Android中跨進(jìn)程通信的一種方式。接下來先要從幾個問題入手冈涧,來帶領(lǐng)大家去理解Binder機(jī)制:

  1. 既然是跨進(jìn)程茂附,那么Client端是如何準(zhǔn)確找到相對應(yīng)的Server端所在的進(jìn)程?
  2. 有一定計(jì)算機(jī)基礎(chǔ)的人都應(yīng)該明白督弓,兩個進(jìn)程間的內(nèi)存是不共享的彩倚,那么Binder又是如何將對象從一個進(jìn)程傳遞到另個一進(jìn)程呢钓试?
  3. 當(dāng)Client持有了遠(yuǎn)端進(jìn)程“對象”時,調(diào)用該對象具體函數(shù)心傀,Server進(jìn)程的對應(yīng)函數(shù)會得到執(zhí)行猜扮,這個也是解釋不通的。

  • 第一個問題:在AIDL轉(zhuǎn)換成的java文件中,有一個常量DESCRIPTOR,這個常量是Binder的唯一標(biāo)識虱痕,一般用當(dāng)前的Binder的類名表示,通過這個常量就可以找到對應(yīng)的進(jìn)程辐赞。
  • 第二個問題:對象是通過序列化在不同進(jìn)程間進(jìn)行通信的,只要對象實(shí)現(xiàn)了Parcelable接口硝训,就可以進(jìn)行序列化响委,Parcelable具體使用不在本文討論范圍。
  • 第三個問題:通過第二個問題我們已經(jīng)了解到窖梁,對象需要進(jìn)行序列化赘风,才能在兩個進(jìn)程間進(jìn)行通信,那也就是說纵刘,兩個進(jìn)程拿到的對象并不是一個對象邀窃,回到第三個問題本身,要實(shí)現(xiàn)跨進(jìn)程的調(diào)用假哎,那么這兩個對象之間肯定是有聯(lián)系的瞬捕,通過什么進(jìn)行聯(lián)系呢,肯定是要通過Binder來進(jìn)行跨進(jìn)程間的調(diào)用舵抹。

首先我們看看我們的程序跨進(jìn)程調(diào)用系統(tǒng)服務(wù)的簡單示例肪虎,實(shí)現(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ù)都是運(yùn)行在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)用的是運(yùn)行在systemServer進(jìn)程中的WindowManager的addView函數(shù)。
從上面代碼可以看出來靠娱,創(chuàng)建一個Window是很簡單的事沧烈,只需要通過WindowManager即可完成,WindowManager是外界訪問Window的入口饱岸,但是具體的實(shí)現(xiàn)掺出,還是要通過WindowManagerService來實(shí)現(xiàn),WindowManager和WindowManagerService是一個掛進(jìn)程的交互苫费,是通過Binder實(shí)現(xiàn)的汤锨。

Binder機(jī)制

先看一下Binder的工作機(jī)制


Binder工作機(jī)制

當(dāng)客戶端發(fā)起遠(yuǎn)程請求時,由于當(dāng)前線程會被掛起直至服務(wù)端進(jìn)程返回數(shù)據(jù)百框,所以如果一個遠(yuǎn)程方法很耗時闲礼,那么就不能在UI線程中發(fā)起此遠(yuǎn)程請求,以防ANR;其次柬泽,由于服務(wù)端的Binder方法運(yùn)行在Binder的線程池中慎菲,所以Binder方法不管是否耗時都應(yīng)該采用同步的方法實(shí)現(xiàn),因?yàn)樗呀?jīng)運(yùn)行在一個線程中了锨并。

客戶端Client調(diào)用Service的函數(shù)Func露该,執(zhí)行的流程如下:


Binder調(diào)用流程圖

通過上面的兩張圖,應(yīng)該能夠清晰的了解到整個Binder的執(zhí)行過程了

Binder的架構(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開機(jī)啟動過程中,Android會初始化系統(tǒng)的各種Service壹瘟,并將這些Service向ServiceManager注冊(即讓ServiceManager管理)鲫剿。客戶端想要得到具體的Service直接向ServiceManager要即可俐筋∏K兀客戶端首先向ServiceManager查詢得到具體的Service引用,然后通過這個引用向具體的服務(wù)端發(fā)送請求澄者,服務(wù)端執(zhí)行完成后就返回笆呆。

先看一下Client、Service粱挡、ServiceManager三者之間的關(guān)系:

Binder架構(gòu)

當(dāng)Service創(chuàng)建的時候赠幕,首先在ServiceManager中注冊一下地址,Client在ServiceManager中查找對應(yīng)Service的地址询筏,然后根據(jù)地址進(jìn)行請求榕堰,Service做出響應(yīng)。
上圖簡單介紹了以下三者時間的關(guān)系嫌套,當(dāng)然真正的關(guān)系不是這么簡單的逆屡,涉及到Framework層、JNI層踱讨、Native層和Kernel層

Binder架構(gòu)

從上圖中可以看到魏蔗,真正實(shí)現(xiàn)跨進(jìn)程通信是在Kernel層。我們平時說Binder相對于其他的進(jìn)程間通信方式的優(yōu)點(diǎn)是痹筛,在多進(jìn)程間傳遞數(shù)據(jù)時莺治,只會進(jìn)行一次數(shù)據(jù)拷貝廓鞠,這樣的說法是是有歧義的。在Kernel層這樣說是對的谣旁,但是從Framework到Kernel床佳,Kernel到Framework都會進(jìn)行內(nèi)存拷貝。也就是上圖的ioctl方法榄审。

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

上述的第三個問題砌们,也就是Binder驅(qū)動實(shí)現(xiàn)的原理,只要能知道上述答案瘟判,那下面就不需要看了怨绣。重新回顧一下這個問題:客戶端持有遠(yuǎn)程進(jìn)程的某個對象引用,然后調(diào)用引用類中的函數(shù)拷获,遠(yuǎn)程進(jìn)程的函數(shù)就執(zhí)行了。學(xué)過操作系統(tǒng)的同學(xué)都知道减细,不同的進(jìn)程之間是不共享資源的匆瓜。也就是說,客戶端持有的這個對象跟遠(yuǎn)程進(jìn)程中的實(shí)際對象完全是兩個不同的對象未蝌⊥灾ǎ客戶端調(diào)用引用的對象跟遠(yuǎn)程進(jìn)程中的對象不是同一個,那問題就來了萧吠,為什么調(diào)用客戶端的這個遠(yuǎn)程對象的引用左冬,遠(yuǎn)端進(jìn)程相應(yīng)的方法就會被調(diào)用?先看下面這幅圖:

image

服務(wù)端跨進(jìn)程的類都要繼承Binder類纸型。我們所持有的Binder引用(即服務(wù)端的類引用)并不是實(shí)際真實(shí)的遠(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)存,并喚醒客戶端線程悲敷。整個過程就完美解釋了兩個進(jìn)程間為什么能夠進(jìn)行通信究恤,所有的實(shí)現(xiàn)都是在Kernel層去實(shí)現(xiàn),屏蔽了細(xì)節(jié)后德,我們只需要實(shí)現(xiàn)Binder接口就可以輕松的實(shí)現(xiàn)跨進(jìn)程通信了部宿。

Binder機(jī)制運(yùn)用

好了,現(xiàn)在對Binder機(jī)制已經(jīng)理解了瓢湃,我們再看看Android是怎么運(yùn)用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對象的引用雾叭,這個引用的真正實(shí)現(xiàn)是WindowManager的某個代理。得到這個引用后落蝙,在調(diào)用addView時织狐,真正的實(shí)現(xiàn)是在代理里面,代理把參數(shù)打包到Parcel對象中筏勒,然后調(diào)用transact函數(shù)(該函數(shù)繼承自Binder)移迫,再觸發(fā)Binder驅(qū)動的一系列調(diào)用過程,在Binder驅(qū)動實(shí)現(xiàn)原理一節(jié)中有具體介紹管行,忘記了的同學(xué)可以返回繼續(xù)看厨埋。關(guān)于Binder的代理對象,可以參考AIDL工具生成的代碼捐顷,這里不再具體介紹荡陷。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市套菜,隨后出現(xiàn)的幾起案子亲善,更是在濱河造成了極大的恐慌,老刑警劉巖逗柴,帶你破解...
    沈念sama閱讀 211,376評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蛹头,死亡現(xiàn)場離奇詭異,居然都是意外死亡戏溺,警方通過查閱死者的電腦和手機(jī)渣蜗,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,126評論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來旷祸,“玉大人耕拷,你說我怎么就攤上這事⊥邢恚” “怎么了骚烧?”我有些...
    開封第一講書人閱讀 156,966評論 0 347
  • 文/不壞的土叔 我叫張陵浸赫,是天一觀的道長。 經(jīng)常有香客問我赃绊,道長既峡,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,432評論 1 283
  • 正文 為了忘掉前任碧查,我火速辦了婚禮运敢,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘忠售。我一直安慰自己传惠,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,519評論 6 385
  • 文/花漫 我一把揭開白布稻扬。 她就那樣靜靜地躺著卦方,像睡著了一般。 火紅的嫁衣襯著肌膚如雪泰佳。 梳的紋絲不亂的頭發(fā)上愿汰,一...
    開封第一講書人閱讀 49,792評論 1 290
  • 那天,我揣著相機(jī)與錄音乐纸,去河邊找鬼。 笑死摇予,一個胖子當(dāng)著我的面吹牛汽绢,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播侧戴,決...
    沈念sama閱讀 38,933評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼宁昭,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了酗宋?” 一聲冷哼從身側(cè)響起积仗,我...
    開封第一講書人閱讀 37,701評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蜕猫,沒想到半個月后寂曹,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,143評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡回右,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,488評論 2 327
  • 正文 我和宋清朗相戀三年隆圆,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片翔烁。...
    茶點(diǎn)故事閱讀 38,626評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡渺氧,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出蹬屹,到底是詐尸還是另有隱情侣背,我是刑警寧澤白华,帶...
    沈念sama閱讀 34,292評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站贩耐,受9級特大地震影響弧腥,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜憔杨,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,896評論 3 313
  • 文/蒙蒙 一鸟赫、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧消别,春花似錦抛蚤、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,742評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至蛇券,卻和暖如春缀壤,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背纠亚。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工塘慕, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蒂胞。 一個月前我還...
    沈念sama閱讀 46,324評論 2 360
  • 正文 我出身青樓图呢,卻偏偏與公主長得像,于是被迫代替她去往敵國和親骗随。 傳聞我的和親對象是個殘疾皇子蛤织,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,494評論 2 348

推薦閱讀更多精彩內(nèi)容