可以說(shuō) Binder 是 Android 底層系統(tǒng)的一個(gè)特色了钓辆,它很好地解決了進(jìn)程間通訊的問(wèn)題跷究。其實(shí)網(wǎng)上有很多介紹 Binder 的文章搜锰,那么本文還是想將 Binder 這部分內(nèi)容細(xì)化一下维贺,更適合于初學(xué)者閱讀告唆。
Binder 產(chǎn)生的背景
首先我們說(shuō)說(shuō)為什么會(huì)出現(xiàn) Binder 這個(gè)東西域携。作為 iOS 開(kāi)發(fā)者簇秒,我還是情不自禁地想去談?wù)?iOS app,事實(shí)上秀鞭,iOS 的每一個(gè) app 都是一個(gè)獨(dú)立的進(jìn)程宰睡,它沒(méi)有 Android 那種比較開(kāi)放的多進(jìn)程通訊能力,甚至 App 與 Extension (如通知中心插件)之間都不能有一種非常直接的通訊方式气筋,當(dāng)然不是說(shuō) iOS 沒(méi)有 IPC 技術(shù)拆内,其實(shí) mach 內(nèi)核也是有著不錯(cuò)的 IPC 技術(shù)的,但這不是本文的重點(diǎn)麸恍。Android 則不太一樣,Android apps 基本上都需要各式各樣的 IPC 需求搀矫,甚至啟動(dòng)一個(gè) Activity 也需要用到 IPC抹沪,有一些 IPC 調(diào)用也許你并不知曉,可能對(duì)開(kāi)發(fā)者最可見(jiàn)的就是用 AIDL 去寫(xiě)一個(gè) Remote Service 接口了瓤球。
Android 很多核心功能都是由一系列 Services 支持的融欧,比如 ActivityService、WindowService 等等等等卦羡,應(yīng)用需要頻繁地與這些 Services 發(fā)生交互噪馏,正是基于這種場(chǎng)景,就亟需一種好的 IPC 解決方案绿饵。
你可能會(huì)想欠肾,為什么不是 Local Socket?或者 Shared Memory拟赊,那是因?yàn)榘踩詿o(wú)法得到保障刺桃。Android 的權(quán)限系統(tǒng)需要一種可靠的方式來(lái)保證各種 Services 的訪(fǎng)問(wèn)是在權(quán)限系統(tǒng)的監(jiān)控下進(jìn)行的,上述提到的解決方案就做不到了吸祟,因?yàn)椴还苁翘捉幼诌€是共享內(nèi)存瑟慈,現(xiàn)有的 Linux 內(nèi)核都不存在一種檢驗(yàn)雙方身份的方法存在桃移,任何通過(guò)套接字或者共享內(nèi)存走的數(shù)據(jù)都可以偽造,而在這個(gè)基礎(chǔ)上做任何驗(yàn)證葛碧,代價(jià)都是相當(dāng)高的借杰。Android 的選擇是基于內(nèi)核,重新開(kāi)發(fā)一套 IPC 機(jī)制吹埠,讓它固有這些特性第步,也就是讓系統(tǒng)可以在 Ring0 級(jí)保障交互雙方身份的正確性,并且這種基于內(nèi)核的方案效率還很高缘琅。
既然要基于內(nèi)核粘都,就一定要對(duì)內(nèi)核動(dòng)手腳,Android 采用驅(qū)動(dòng)的方式實(shí)現(xiàn)這個(gè)技術(shù)刷袍,而不是直接修改 Linux 內(nèi)核翩隧。這樣你就可以假設(shè),手機(jī)中有一個(gè)“設(shè)備”呻纹,應(yīng)用之間通過(guò)這個(gè)設(shè)備來(lái)交互堆生,而這個(gè)設(shè)備自身有一套身份校驗(yàn)機(jī)制,這樣就比基于用戶(hù)態(tài)的 IPC 方案來(lái)的安全得多雷酪,也快得多了淑仆。
Binder 是怎么工作的
我們暫且不需要深入理解 Binder 驅(qū)動(dòng)底層的實(shí)現(xiàn),也不需要知道 Binder 驅(qū)動(dòng)提供了什么接口哥力,我們就來(lái)看看一個(gè) app 是如何通過(guò) Binder 這個(gè)機(jī)制來(lái)實(shí)現(xiàn)跨進(jìn)程通信的蔗怠。
到這里,你可以把 Binder 驅(qū)動(dòng)看作一個(gè)機(jī)器吩跋,它連接著所有 Services(假設(shè) app 只調(diào)用 Services 提供的接口)寞射。
一個(gè) app(客戶(hù)端)想要找一個(gè) Service 辦點(diǎn)事,它就要去操作這個(gè)機(jī)器锌钮,而這個(gè)機(jī)器會(huì)檢測(cè) app 的身份桥温,如果身份合法,則可以繼續(xù)操作梁丘。假設(shè) app 要調(diào)用 A 服務(wù)的 foo
函數(shù)侵浸,那么它可以告訴這個(gè)機(jī)器,這個(gè)機(jī)器隨后就會(huì)檢查連在它身上的所有 Services兰吟,找到 app 需要的那一個(gè)通惫,讓它執(zhí)行 foo
函數(shù),并且再將返回值告訴 app混蔼,這樣 app 就成功隔著進(jìn)程操作到 A 服務(wù)了。
這當(dāng)然是很抽象的說(shuō)法珊燎,系統(tǒng)在 Framework 層做了很好地封裝惭嚣,讓我們可以友好地使用 Binder 驅(qū)動(dòng)來(lái)進(jìn)行 IPC 操作遵湖,下面我們就直接看應(yīng)用層所提供的接口是如何工作的。
探究與 Binder 相關(guān)的 Java 類(lèi)
要說(shuō)這些類(lèi)晚吞,我們首先要用到它們延旧,最簡(jiǎn)單的方式就是去創(chuàng)建一個(gè) Service,讓它運(yùn)行在單獨(dú)的進(jìn)程中槽地,然后編寫(xiě) AIDL迁沫,實(shí)現(xiàn)一個(gè)接口,再到 Activity 中去使用這個(gè)接口捌蚊。(注意:本文不介紹 Remote Service 與 AIDL 的相關(guān)知識(shí)集畅,如果讀者對(duì)這部分內(nèi)容還不夠了解,請(qǐng)先將它們弄懂再回來(lái)看本文)
AIDL 代碼生成器為我們創(chuàng)建了一個(gè) java 文件缅糟,這里面涉及到幾個(gè)類(lèi):
這些就是在應(yīng)用層使用 Binder 所需的所有類(lèi)了挺智,看類(lèi)圖有些錯(cuò)綜,但實(shí)際還是很簡(jiǎn)單的窗宦。
AIDL 生成的就是 IMyService
這個(gè)接口赦颇,而 Stub
和 Proxy
則是這個(gè)接口的兩個(gè)內(nèi)部類(lèi),分別是 Binder
類(lèi)和 BinderProxy
類(lèi)的子類(lèi)(Proxy
類(lèi)雖然是用組合方式來(lái)持有 BinderProxy
的赴涵,但實(shí)際就是在直接用這個(gè)類(lèi)媒怯,只不過(guò)做了一層封裝,讓其更易使用而已)髓窜,Stub
和 Proxy
都實(shí)現(xiàn)了 IMyService
扇苞。
所以 IInterface
到底是什么,它就是一個(gè)用于表達(dá) Service
提供的功能的一個(gè)契約纱烘,也就是說(shuō) IInterface
里有的方法杨拐,Service
都能提供,調(diào)用者你別管用的是 BinderProxy
還是什么擂啥,只要拿到 IInterface
哄陶,你就可以直接調(diào)用里面的方法,它就是一個(gè)接口哺壶。
同時(shí) Stub
雖然實(shí)現(xiàn)了 IMyService
屋吨,但是并沒(méi)有實(shí)現(xiàn)厘米的任何方法,它是一個(gè)抽象類(lèi)山宾,開(kāi)發(fā)者需要自己子類(lèi)化 Stub
去實(shí)現(xiàn)具體的功能至扰。
Proxy
實(shí)現(xiàn)了 IMyService
,并且實(shí)現(xiàn)了里面的方法资锰,這些方法的實(shí)現(xiàn)我們下面再說(shuō)敢课。
為什么 IMyService
要分 Stub
和 Proxy
呢?這是為了要適用于本地調(diào)用和遠(yuǎn)程調(diào)用兩種情況。如果 Service 運(yùn)行在同一個(gè)進(jìn)程直秆,那就直接用 Stub
濒募,因?yàn)樗苯訉?shí)現(xiàn)了 Service 提供的功能,不需要任何 IPC 過(guò)程圾结。如果 Service 運(yùn)行在其他進(jìn)程瑰剃,那客戶(hù)端使用的就是 Proxy
,這里這個(gè) Proxy
的功能大家應(yīng)該能想到了吧筝野,就是把參數(shù)封裝后發(fā)送給 Binder 驅(qū)動(dòng)晌姚,然后執(zhí)行一系列 IPC 操作最后再取出結(jié)果返回。
好歇竟,到這里請(qǐng)求用 Proxy
發(fā)出去了挥唠,Service 怎么接受請(qǐng)求并作出響應(yīng)呢,這就需要 Stub
了途蒋,還記得我們的 Stub
是繼承自 Binder
的嗎猛遍?Binder
有一個(gè) onTransact
方法,而 Stub
重寫(xiě)了這個(gè)函數(shù)号坡,這個(gè)函數(shù)三個(gè)重要參數(shù):int code
懊烤、Parcel data
、Parcel reply
宽堆,分別對(duì)應(yīng)了被調(diào)函數(shù)編號(hào)腌紧、參數(shù)包、響應(yīng)包畜隶。當(dāng) Proxy
發(fā)起了一個(gè)請(qǐng)求壁肋,服務(wù)端中相應(yīng)的響應(yīng)線(xiàn)程就會(huì)通過(guò) JNI 調(diào)用到 Stub
類(lèi),然后執(zhí)行里面的 execTransact
方法籽慢,進(jìn)而轉(zhuǎn)到 onTransact
方法浸遗。(這一系列調(diào)用鏈大家可以從源碼中分析出來(lái),我這里作為拋磚引玉箱亿,就不帶大家分析 native 層的代碼了)
我們來(lái)看一眼這個(gè) onTransact
:
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
switch (code) {
case INTERFACE_TRANSACTION:
{
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_increaseCounter:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
int _result = this.increaseCounter(_arg0);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
是不是非常清晰易懂跛锌,就是根據(jù)傳過(guò)來(lái)的數(shù)據(jù)包來(lái)做相應(yīng)的操作,然后把結(jié)果寫(xiě)回?cái)?shù)據(jù)包届惋,Binder 驅(qū)動(dòng)會(huì)來(lái)幫我們做好這些數(shù)據(jù)包的分發(fā)工作髓帽。而這段代碼是運(yùn)行在 Service 本地進(jìn)程中的,它可以直接調(diào)用實(shí)現(xiàn)好的 Stub 類(lèi)中的相關(guān)方法(本例子中是 increaseCounter
方法)脑豹。
下面我們趁熱打鐵再來(lái)看一眼 Proxy
類(lèi)中的 increaseCounter
是怎么實(shí)現(xiàn)的:
@Override
public int increaseCounter(int increment) throws RemoteException{
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(increment);
mRemote.transact(Stub.TRANSACTION_increaseCounter, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
正好與 Stub
中的處理能夠?qū)?yīng)起來(lái)郑藏,其實(shí)這兩段代碼就是整個(gè) IPC 的核心了,Binder 驅(qū)動(dòng)和 Binder 類(lèi)在底層幫我們做好了其他一切事情瘩欺。
休息一下必盖。
下面我們來(lái)思考另一件事情拌牲,如何判斷 Service 運(yùn)行在同一進(jìn)程還是不同進(jìn)程?
我們知道筑悴,Service
有一個(gè) onBind
方法们拙,這里面就返回了我們實(shí)現(xiàn)好的 Stub
類(lèi)稍途,而客戶(hù)端 bind service 時(shí)拿到的又是一個(gè) IBinder
對(duì)象阁吝,我們每次只需要調(diào)用 Stub
的 asInterface
靜態(tài)方法,把這個(gè) IBinder
對(duì)象傳進(jìn)去就能拿到 Stub
類(lèi)或者 Proxy
類(lèi)了械拍,看起來(lái)十分智能突勇!那么這個(gè) asInterface
又蘊(yùn)藏什么玄機(jī)呢?我們來(lái)看一眼實(shí)現(xiàn)代碼:
public static IBackgroundService asInterface(IBinder obj){
if ((obj==null)) {
return null;
}
IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof IBackgroundService))) {
return ((IBackgroundService) iin);
}
return new IBackgroundService.Stub.Proxy(obj);
}
是不是有點(diǎn)莫名其妙坷虑,queryLocalInterface
是什么鬼甲馋?
我們?cè)賮?lái)看看 queryLocalInterface
的實(shí)現(xiàn):
public IInterface queryLocalInterface(String descriptor) {
if (mDescriptor.equals(descriptor)) {
return mOwner;
}
return null;
}
它判斷了一下 descriptor
參數(shù)是否與自身 owner
的 descriptor
一致,如果一致就直接返回 owner
迄损,那么 owner
和 descriptor
是在哪被設(shè)置的呢定躏,就是在 Stub
的構(gòu)造函數(shù)中被設(shè)置的。
于是乎芹敌,如果 Service 運(yùn)行在同一進(jìn)程痊远,那么客戶(hù)端拿到的 IBinder
就是 Stub
類(lèi),而 Stub
的 queryLocalInterface
又會(huì)返回自己氏捞;而 Service 運(yùn)行在單獨(dú)進(jìn)程中時(shí)碧聪,客戶(hù)端拿到的 IBinder
就是系統(tǒng)提供好的 BinderProxy
,BinderProxy
中的 queryLocalInterface
默認(rèn)直接返回 null
液茎,根據(jù)代碼逞姿,asInterface
就會(huì)構(gòu)造一個(gè) Proxy
返回給客戶(hù)端,那么接下來(lái)的故事就是上面我們講過(guò)的了捆等。
自己利用 Binder 來(lái)進(jìn)行 IPC
有了上面的基礎(chǔ)滞造,其實(shí)我們完全不需要 AIDL 了有木有,自己用 Binder
類(lèi)和 BinderProxy
類(lèi)就完全可以實(shí)現(xiàn) Service 與客戶(hù)端的通訊栋烤,下面我就速速寫(xiě)一個(gè)簡(jiǎn)單的例子谒养。
Service 中的 onBinder 方法我這樣實(shí)現(xiàn):
客戶(hù)端就這樣:
我們甚至不必按照 AIDL 的通信規(guī)范,自己處理 data 和 reply 也是完全可以的班缎,但這只是為了演示 Binder 的原理蝴光,日常開(kāi)發(fā)中還是不要這么做了吧。
小結(jié)
Binder 的設(shè)計(jì)非常優(yōu)秀达址,分析 Binder 的實(shí)現(xiàn)也是一件十分有趣的事情蔑祟,本文作為對(duì) Binder 深入研究的「引入」,站在了一個(gè)很高的角度俯視整套系統(tǒng)沉唠,并沒(méi)有對(duì)其中的細(xì)節(jié)做深入探討疆虚,如果大家對(duì)內(nèi)核開(kāi)發(fā)或者操作系統(tǒng)底層有興趣的話(huà),也可以去看看 Binder 驅(qū)動(dòng)的實(shí)現(xiàn),相信也會(huì)有不少收獲径簿,本文到這里就先結(jié)束了 ;-)