1 概述
Android系統(tǒng)中,涉及到多進程間的通信底層都是依賴于Binder IPC機制。例如當(dāng)進程A中的Activity要向進程B中的Service通信,這便需要依賴于Binder IPC。不僅于此,整個Android系統(tǒng)架構(gòu)中,大量采用了Binder機制作為IPC(進程間通信)方案。
當(dāng)然也存在部分其他的IPC方式,如管道
、SystemV
、Socket
等。那么Android為什么不使用這些原有的技術(shù),而是要使開發(fā)一種新的叫Binder的進程間通信機制呢?
為什么要使用Binder?
性能方面
在移動設(shè)備上(性能受限制的設(shè)備,比如要省電),廣泛地使用跨進程通信對通信機制的性能有嚴(yán)格的要求,Binder相對于傳統(tǒng)的Socket方式,更加高效。Binder數(shù)據(jù)拷貝只需要一次,而管道、消息隊列、Socket都需要2次,共享內(nèi)存方式一次內(nèi)存拷貝都不需要,但實現(xiàn)方式又比較復(fù)雜。安全方面
傳統(tǒng)的進程通信方式對于通信雙方的身份并沒有做出嚴(yán)格的驗證,比如Socket通信ip地址是客戶端手動填入,很容易進行偽造,而Binder機制從協(xié)議本身就支持對通信雙方做身份校檢,因而大大提升了安全性。
1.1 基本概念介紹
這里我們先從 Linux 中進程間通信涉及的一些基本概念開始介紹,然后逐步展開,向大家說明傳統(tǒng)的進程間通信的原理。
上圖展示了 Liunx 中跨進程通信涉及到的一些基本概念:
進程隔離
簡單的說就是操作系統(tǒng)中,進程與進程間內(nèi)存是不共享的。兩個進程就像兩個平行的世界,A 進程沒法直接訪問 B 進程的數(shù)據(jù),這就是進程隔離的通俗解釋。A 進程和 B 進程之間要進行數(shù)據(jù)交互就得采用特殊的通信機制:進程間通信(IPC)。進程空間劃分:用戶空間(User Space)/內(nèi)核空間(Kernel Space)
現(xiàn)在操作系統(tǒng)都是采用的虛擬存儲器,對于 32 位系統(tǒng)而言,它的尋址空間(虛擬存儲空間)就是 2 的 32 次方,也就是 4GB。操作系統(tǒng)的核心是內(nèi)核,獨立于普通的應(yīng)用程序,可以訪問受保護的內(nèi)存空間,也可以訪問底層硬件設(shè)備的權(quán)限。為了保護用戶進程不能直接操作內(nèi)核,保證內(nèi)核的安全,操作系統(tǒng)從邏輯上將虛擬空間劃分為用戶空間(User Space)和內(nèi)核空間(Kernel Space)。針對 Linux 操作系統(tǒng)而言,將最高的 1GB 字節(jié)供內(nèi)核使用,稱為內(nèi)核空間;較低的 3GB 字節(jié)供各進程使用,稱為用戶空間。
簡單的說就是,內(nèi)核空間(Kernel)是系統(tǒng)內(nèi)核運行的空間,用戶空間(User Space)是用戶程序運行的空間。為了保證安全性,它們之間是隔離的。
-
系統(tǒng)調(diào)用:用戶態(tài)/內(nèi)核態(tài)
雖然從邏輯上進行了用戶空間和內(nèi)核空間的劃分,但不可避免的用戶空間需要訪問內(nèi)核資源,比如文件操作、訪問網(wǎng)絡(luò)等等。為了突破隔離限制,就需要借助系統(tǒng)調(diào)用來實現(xiàn)。系統(tǒng)調(diào)用是用戶空間訪問內(nèi)核空間的唯一方式,保證了所有的資源訪問都是在內(nèi)核的控制下進行的,避免了用戶程序?qū)ο到y(tǒng)資源的越權(quán)訪問,提升了系統(tǒng)安全性和穩(wěn)定性。Linux 使用兩級保護機制:0 級供系統(tǒng)內(nèi)核使用,3 級供用戶程序使用。
當(dāng)一個任務(wù)(進程)執(zhí)行系統(tǒng)調(diào)用而陷入內(nèi)核代碼中執(zhí)行時,稱進程處于內(nèi)核運行態(tài)(內(nèi)核態(tài))。此時處理器處于特權(quán)級最高的(0級)內(nèi)核代碼中執(zhí)行。當(dāng)進程處于內(nèi)核態(tài)時,執(zhí)行的內(nèi)核代碼會使用當(dāng)前進程的內(nèi)核棧。每個進程都有自己的內(nèi)核棧。
當(dāng)進程在執(zhí)行用戶自己的代碼的時候,我們稱其處于用戶運行態(tài)(用戶態(tài))。此時處理器在特權(quán)級最低的(3級)用戶代碼中運行。
系統(tǒng)調(diào)用主要通過如下兩個函數(shù)來實現(xiàn):
copy_from_user() //將數(shù)據(jù)從用戶空間拷貝到內(nèi)核空間
copy_to_user() //將數(shù)據(jù)從內(nèi)核空間拷貝到用戶空間
1.2 Linux 下的傳統(tǒng) IPC 通信原理
理解了上面的幾個概念,我們再來看看傳統(tǒng)的 IPC 方式中,進程之間是如何實現(xiàn)通信的。
通常的做法是消息發(fā)送方將要發(fā)送的數(shù)據(jù)存放在內(nèi)存緩存區(qū)中,通過系統(tǒng)調(diào)用進入內(nèi)核態(tài)。然后內(nèi)核程序在內(nèi)核空間分配內(nèi)存,開辟一塊內(nèi)核緩存區(qū),調(diào)用 copyfromuser() 函數(shù)將數(shù)據(jù)從用戶空間的內(nèi)存緩存區(qū)拷貝到內(nèi)核空間的內(nèi)核緩存區(qū)中。同樣的,接收方進程在接收數(shù)據(jù)時在自己的用戶空間開辟一塊內(nèi)存緩存區(qū),然后內(nèi)核程序調(diào)用 copytouser() 函數(shù)將數(shù)據(jù)從內(nèi)核緩存區(qū)拷貝到接收進程的內(nèi)存緩存區(qū)。這樣數(shù)據(jù)發(fā)送方進程和數(shù)據(jù)接收方進程就完成了一次數(shù)據(jù)傳輸,我們稱完成了一次進程間通信。如下圖:
這種傳統(tǒng)的 IPC 通信方式有兩個問題:
- 性能低下,一次數(shù)據(jù)傳遞需要經(jīng)歷:內(nèi)存緩存區(qū) --> 內(nèi)核緩存區(qū) --> 內(nèi)存緩存區(qū),需要 2 次數(shù)據(jù)拷貝;
- 接收數(shù)據(jù)的緩存區(qū)由數(shù)據(jù)接收進程提供,但是接收進程并不知道需要多大的空間來存放將要傳遞過來的數(shù)據(jù),因此只能開辟盡可能大的內(nèi)存空間或者先調(diào)用 API 接收消息頭來獲取消息體的大小,這兩種做法不是浪費空間就是浪費時間。
2. Binder 跨進程通信原理
理解了 Linux IPC 相關(guān)概念和通信原理,接下來我們正式介紹下 Binder IPC 的原理。
2.1 動態(tài)內(nèi)核可加載模塊 && 內(nèi)存映射
正如前面所說,跨進程通信是需要內(nèi)核空間做支持的。傳統(tǒng)的 IPC 機制如管道、Socket 都是內(nèi)核的一部分,因此通過內(nèi)核支持來實現(xiàn)進程間通信自然是沒問題的。但是 Binder 并不是 Linux 系統(tǒng)內(nèi)核的一部分,那怎么辦呢?這就得益于 Linux 的動態(tài)內(nèi)核可加載模塊(Loadable Kernel Module,LKM)的機制;模塊是具有獨立功能的程序,它可以被單獨編譯,但是不能獨立運行。它在運行時被鏈接到內(nèi)核作為內(nèi)核的一部分運行。這樣,Android 系統(tǒng)就可以通過動態(tài)添加一個內(nèi)核模塊運行在內(nèi)核空間,用戶進程之間通過這個內(nèi)核模塊作為橋梁來實現(xiàn)通信。
在 Android 系統(tǒng)中,這個運行在內(nèi)核空間,負責(zé)各個用戶進程通過 Binder 實現(xiàn)通信的內(nèi)核模塊就叫 Binder 驅(qū)動(Binder Dirver)
那么在 Android 系統(tǒng)中用戶進程之間是如何通過這個內(nèi)核模塊(Binder 驅(qū)動)來實現(xiàn)通信的呢?難道是和前面說的傳統(tǒng) IPC 機制一樣,先將數(shù)據(jù)從發(fā)送方進程拷貝到內(nèi)核緩存區(qū),然后再將數(shù)據(jù)從內(nèi)核緩存區(qū)拷貝到接收方進程,通過兩次拷貝來實現(xiàn)嗎?顯然不是,否則也不會有開篇所說的 Binder 在性能方面的優(yōu)勢了。
這就不得不通道 Linux 下的另一個概念:內(nèi)存映射。
Binder IPC 機制中涉及到的內(nèi)存映射通過 mmap() 來實現(xiàn),mmap() 是操作系統(tǒng)中一種內(nèi)存映射的方法。內(nèi)存映射簡單的講就是將用戶空間的一塊內(nèi)存區(qū)域映射到內(nèi)核空間。映射關(guān)系建立后,用戶對這塊內(nèi)存區(qū)域的修改可以直接反應(yīng)到內(nèi)核空間;反之內(nèi)核空間對這段區(qū)域的修改也能直接反應(yīng)到用戶空間。
內(nèi)存映射能減少數(shù)據(jù)拷貝次數(shù),實現(xiàn)用戶空間和內(nèi)核空間的高效互動。兩個空間各自的修改能直接反映在映射的內(nèi)存區(qū)域,從而被對方空間及時感知。也正因為如此,內(nèi)存映射能夠提供對進程間通信的支持。
2.2 Binder IPC 實現(xiàn)原理
Binder IPC 正是基于內(nèi)存映射(mmap)來實現(xiàn)的,但是 mmap() 通常是用在有物理介質(zhì)的文件系統(tǒng)上的。
比如進程中的用戶區(qū)域是不能直接和物理設(shè)備打交道的,如果想要把磁盤上的數(shù)據(jù)讀取到進程的用戶區(qū)域,需要兩次拷貝(磁盤-->內(nèi)核空間-->用戶空間);通常在這種場景下 mmap() 就能發(fā)揮作用,通過在物理介質(zhì)和用戶空間之間建立映射,減少數(shù)據(jù)的拷貝次數(shù),用內(nèi)存讀寫取代I/O讀寫,提高文件讀取效率。
而 Binder 并不存在物理介質(zhì),因此 Binder 驅(qū)動使用 mmap() 并不是為了在物理介質(zhì)和用戶空間之間建立映射,而是用來在內(nèi)核空間創(chuàng)建數(shù)據(jù)接收的緩存空間。
一次完整的 Binder IPC 通信過程通常是這樣:
- 首先 Binder 驅(qū)動在內(nèi)核空間創(chuàng)建一個數(shù)據(jù)接收緩存區(qū);
- 接著在內(nèi)核空間開辟一塊內(nèi)核緩存區(qū),建立內(nèi)核緩存區(qū)和內(nèi)核中數(shù)據(jù)接收緩存區(qū)之間的映射關(guān)系,以及內(nèi)核中數(shù)據(jù)接收緩存區(qū)和接收進程用戶空間地址的映射關(guān)系;
- 發(fā)送方進程通過系統(tǒng)調(diào)用 copyfromuser() 將數(shù)據(jù) copy 到內(nèi)核中的內(nèi)核緩存區(qū),由于內(nèi)核緩存區(qū)和接收進程的用戶空間存在內(nèi)存映射,因此也就相當(dāng)于把數(shù)據(jù)發(fā)送到了接收進程的用戶空間,這樣便完成了一次進程間的通信。
3. Binder 通信模型
介紹完 Binder IPC 的底層通信原理,接下來我們看看實現(xiàn)層面是如何設(shè)計的。
一次完整的進程間通信必然至少包含兩個進程,通常我們稱通信的雙方分別為客戶端進程(Client)和服務(wù)端進程(Server),由于進程隔離機制的存在,通信雙方必然需要借助 Binder 來實現(xiàn)。
3.1 Client/Server/ServiceManager/驅(qū)動
前面我們介紹過,Binder 是基于 C/S 架構(gòu)的。由一系列的組件組成,包括 Client、Server、ServiceManager、Binder 驅(qū)動。其中 Client、Server、Service Manager 運行在用戶空間,Binder 驅(qū)動運行在內(nèi)核空間。其中 Service Manager 和 Binder 驅(qū)動由系統(tǒng)提供,而 Client、Server 由應(yīng)用程序來實現(xiàn)。Client、Server 和 ServiceManager 均是通過系統(tǒng)調(diào)用 open、mmap 和 ioctl 來訪問設(shè)備文件 /dev/binder,從而實現(xiàn)與 Binder 驅(qū)動的交互來間接的實現(xiàn)跨進程通信。
Server創(chuàng)建了Binder實體,為其取一個字符形式,可讀易記的名字,將這個Binder連同名字以數(shù)據(jù)包的形式通過Binder驅(qū)動發(fā)送給ServiceManager,通知ServiceManager注冊一個名字為XX的Binder,它位于Server中。驅(qū)動為這個穿過進程邊界的Binder創(chuàng)建位于內(nèi)核中的實體結(jié)點以及ServiceManager對實體的引用,將名字以及新建的引用打包給ServiceManager。ServiceManager收數(shù)據(jù)包后,從中取出名字和引用填入一張查找表中。但是一個Server若向ServiceManager注冊自己Binder就必須通過0這個引用和ServiceManager的Binder通信。Server向ServiceManager注冊了Binder實體及其名字后,Client就可以通過名字獲得該Binder的引用了。Clent也利用保留的0號引用向ServiceManager請求訪問某個Binder:我申請名字叫XX的Binder的引用。ServiceManager收到這個連接請求,從請求數(shù)據(jù)包里獲得Binder的名字,在查找表里找到該名字對應(yīng)的條目,從條目中取出Binder引用,將該引用作為回復(fù)發(fā)送給發(fā)起請求的Client。
當(dāng)然,不是所有的Binder都需要注冊給ServiceManager廣而告之的。Server端可以通過已經(jīng)建立的Binder連接將創(chuàng)建的Binder實體傳給Client,當(dāng)然這條已經(jīng)建立的Binder連接必須是通過實名Binder實現(xiàn)。由于這個Binder沒有向ServiceManager注冊名字,所以是匿名Binder。Client將會收到這個匿名Binder的引用,通過這個引用向位于Server中的實體發(fā)送請求。匿名Binder為通信雙方建立一條私密通道,只要Server沒有把匿名Binder發(fā)給別的進程,別的進程就無法通過窮舉或猜測等任何方式獲得該Binder的引用,向該Binder發(fā)送請求。
Client進程:使用服務(wù)的進程。
Server進程:提供服務(wù)的進程。
ServiceManager進程:ServiceManager的作用是將字符形式的Binder名字轉(zhuǎn)化成Client中對該Binder的引用,使得Client能夠通過Binder名字獲得對Server中Binder實體的引用。
Binder驅(qū)動:驅(qū)動負責(zé)進程之間Binder通信的建立,Binder在進程之間的傳遞,Binder引用計數(shù)管理,數(shù)據(jù)包在進程之間的傳遞和交互等一系列底層支持。
Binder運行機制
圖中Client/Server/ServiceManage之間的相互通信都是基于Binder機制。既然基于Binder機制通信,那么同樣也是C/S架構(gòu),則圖中的3大步驟都有相應(yīng)的Client端與Server端。
注冊服務(wù)(addService):Server進程要先注冊Service到ServiceManager。該過程:Server是客戶端,ServiceManager是服務(wù)端。
獲取服務(wù)(getService):Client進程使用某個Service前,須先向ServiceManager中獲取相應(yīng)的Service。該過程:Client是客戶端,ServiceManager是服務(wù)端。
使用服務(wù):Client根據(jù)得到的Service信息建立與Service所在的Server進程通信的通路,然后就可以直接與Service交互。該過程:client是客戶端,server是服務(wù)端。
圖中的Client,Server,Service Manager之間交互都是虛線表示,是由于它們彼此之間不是直接交互的,而是都通過與Binder驅(qū)動進行交互的,從而實現(xiàn)IPC通信方式。其中Binder驅(qū)動位于內(nèi)核空間,Client,Server,Service Manager位于用戶空間。Binder驅(qū)動和Service Manager可以看做是Android平臺的基礎(chǔ)架構(gòu),而Client和Server是Android的應(yīng)用層,開發(fā)人員只需自定義實現(xiàn)client、Server端,借助Android的基本平臺架構(gòu)便可以直接進行IPC通信。
Binder運行的實例解釋
首先我們看看我們的程序跨進程調(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);
注冊服務(wù)(addService):在Android開機啟動過程中,Android會初始化系統(tǒng)的各種Service,并將這些Service向ServiceManager注冊(即讓ServiceManager管理)。這一步是系統(tǒng)自動完成的。
獲取服務(wù)(getService):客戶端想要得到具體的Service直接向ServiceManager要即可??蛻舳耸紫认騍erviceManager查詢得到具體的Service引用,通常是Service引用的代理對象,對數(shù)據(jù)進行一些處理操作。即第2行代碼中,得到的wm是WindowManager對象的引用。
使用服務(wù):通過這個引用向具體的服務(wù)端發(fā)送請求,服務(wù)端執(zhí)行完成后就返回。即第6行調(diào)用WindowManager的addView函數(shù),將觸發(fā)遠程調(diào)用,調(diào)用的是運行在systemServer進程中的WindowManager的addView函數(shù)。
使用服務(wù)的具體執(zhí)行過程
- client通過獲得一個server的代理接口,對server進行調(diào)用。
- 代理接口中定義的方法與server中定義的方法時一一對應(yīng)的。
- client調(diào)用某個代理接口中的方法時,代理接口的方法會將client傳遞的參數(shù)打包成Parcel對象。
- 代理接口將Parcel發(fā)送給內(nèi)核中的binder driver。
- server會讀取binder driver中的請求數(shù)據(jù),如果是發(fā)送給自己的,解包Parcel對象,處理并將結(jié)果返回。
- 整個的調(diào)用過程是一個同步過程,在server處理的時候,client會block住。因此client調(diào)用過程不應(yīng)在主線程
4. binder介紹
Android源碼中,binder的核心庫是在native層實現(xiàn),但在java層和native層都有接口供應(yīng)用程序使用。如果單從binder角度出發(fā),Binder架構(gòu)圖如下:
(1) binder驅(qū)動層
Android因此添加了binder驅(qū)動,其設(shè)備節(jié)點為/dev/binder,主設(shè)備號為10,binder驅(qū)動程序在內(nèi)核中的頭文件和代碼路徑如下:
kernel/drivers/staging/binder.h
kernel/drivers/staging/binder.c
binder驅(qū)動層的主要作用是完成實際的binder數(shù)據(jù)傳輸。
(2) binder adapter層
主要是IPCThreadState.cpp和ProcessState.cpp,源碼位于frameworks/native/libs/binder目錄下,這兩個類都采用了單例模式,主要負責(zé)和驅(qū)動直接交互。
a、ProcessState,進程相關(guān)的類,負責(zé)打開binder設(shè)備,進行一些初始化設(shè)置并做內(nèi)存映射;
void ProcessState::startThreadPool()
該方法啟動的新線程,并通過joinThreadPool讀取binder設(shè)備,查看是否有請求
b、IPCThreadState,線程相關(guān)的類,負責(zé)直接和binder設(shè)備通信,使用ioctl讀寫binder驅(qū)動數(shù)據(jù)。
status_t IPCThreadState::talkWithDriver(bool doReceive)
該方法調(diào)用的是ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr)從/dev/binder讀取。
status_t IPCThreadState::executeCommand(int32_t cmd)
該方法是對talkWithDriver返回的數(shù)據(jù)進行處理。
void IPCThreadState::joinThreadPool(bool isMain)
該方法創(chuàng)建線程并進行talkWithDriver以及executeCommand。
(3) Binder核心層
Binder核心層主要是IBinder及它的兩個子類,即BBinder和BpBinder,分別代表了最基本的服務(wù)端及客戶端。源碼位于frameworks/native/libs/binder目錄下。
binder service服務(wù)端實體類會繼承BnInterface,而BnInterface會繼承自BBinder,服務(wù)端可將BBinder對象注冊到servicemananger進程。
客戶端程序和驅(qū)動交互時只能得到遠程對象的句柄handle,它可以調(diào)用調(diào)用ProcessState的getStrongProxyForHandle函數(shù),利用句柄handle建立BpBinder對象,然后將它轉(zhuǎn)為IBinder指針返回給調(diào)用者。這樣客戶端每次調(diào)用IBinder指針的transact方法,其實是執(zhí)行BpBinder的transact方法。
(4) Binder框架層
a、Native Binder框架層包含以下類(frameworks/native/libs/binder):IInterface,BnInterface,BpInterface,等。
b、Java框架層包含以下類(frameworks/base/core/java/android/os):
IBinder,Binder,IInterface,ServiceManagerNative,ServiceManager,BinderInternal,IServiceManager,ServiceManagerProxy
Java框架層的類的部分方法的實現(xiàn)在本地代碼里(frameworks/base/core/jni)。
4.1 Binder類分層
整個Binder從kernel至,native,JNI,F(xiàn)ramework層所涉及的類如下:
【初始化】
Zygote啟動時會有一個虛擬機注冊過程,該過程調(diào)用AndroidRuntime::startReg方法來完成jni方法的注冊。調(diào)用register_Android_os_Binder完成binder的native方法注冊。
int register_Android_os_Binder(JNIEnv* env)
{
// 注冊Binder類的jni方法
if (int_register_Android_os_Binder(env) < 0)
return -1;
// 注冊BinderInternal類的jni方法
if (int_register_Android_os_BinderInternal(env) < 0)
return -1;
// 注冊BinderProxy類的jni方法
if (int_register_Android_os_BinderProxy(env) < 0)
return -1;
...
return 0;
}
4.2 binder調(diào)用順序
使用AIDL進行跨進程的通信,實際上就是使用binder,必須要繼承binder接口,java和C++都有對應(yīng)的IBinder,proxy,stub等,通過jni進程數(shù)據(jù)的交互,binder的核心在native層。整個調(diào)用關(guān)系如下。
5. Binder的Proxy-Stub模式
5.1 Java空間
IBinder/Binder/BinderProxy是Binder機制的核心api, 而IInterface和AIDL就是為了方便開發(fā)者使用Binder進行進程間通信。先看IBinder接口:
public interface IBinder
{
. . . . . .
public String getInterfaceDescriptor() throws RemoteException;
public boolean pingBinder();
public boolean isBinderAlive();
public IInterface queryLocalInterface(String descriptor); //返回IInterface類型
public void dump(FileDescriptor fd, String[] args) throws RemoteException;
public void dumpAsync(FileDescriptor fd, String[] args) throws RemoteException;
public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException; //通信函數(shù)
public interface DeathRecipient
{
public void binderDied();
}
public void linkToDeath(DeathRecipient recipient, int flags)throws RemoteException;
public boolean unlinkToDeath(DeathRecipient recipient, int flags);
}
Binder與BinderProxy的定義如下,都實現(xiàn)了IBinder接口
public class Binder implements IBinder {
...
public Binder() {
init();
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Binder> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Binder class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
}
public void attachInterface(IInterface owner, String descriptor) {
mOwner = owner;
mDescriptor = descriptor;
}
public String getInterfaceDescriptor() {
return mDescriptor;
}
public IInterface queryLocalInterface(String descriptor) {
if (mDescriptor.equals(descriptor)) {
return mOwner;
}
return null;
}
protected boolean onTransact(int code, Parcel data, Parcel reply,
int flags) throws RemoteException {
if (code == INTERFACE_TRANSACTION) {
reply.writeString(getInterfaceDescriptor());
return true;
} else if (code == DUMP_TRANSACTION) {
ParcelFileDescriptor fd = data.readFileDescriptor();
String[] args = data.readStringArray();
if (fd != null) {
try {
dump(fd.getFileDescriptor(), args);
} finally {
try {
fd.close();
} catch (IOException e) {
// swallowed, not propagated back to the caller
}
}
}
// Write the StrictMode header.
if (reply != null) {
reply.writeNoException();
} else {
StrictMode.clearGatheredViolations();
}
return true;
}
return false;
}
public final boolean transact(int code, Parcel data, Parcel reply,
int flags) throws RemoteException {
if (false) Log.v("Binder", "Transact: " + code + " to " + this);
if (data != null) {
data.setDataPosition(0);
}
boolean r = onTransact(code, data, reply, flags);//調(diào)用onTransact()函數(shù)
if (reply != null) {
reply.setDataPosition(0);
}
return r;
}
public void linkToDeath(DeathRecipient recipient, int flags) {
}
public boolean unlinkToDeath(DeathRecipient recipient, int flags) {
return true;
}
protected void finalize() throws Throwable {
try {
destroy();
} finally {
super.finalize();
}
}
private native final void init();
private native final void destroy();
// Entry point from android_util_Binder.cpp's onTransact
private boolean execTransact(int code, long dataObj, long replyObj, int flags) {
...
try {
res = onTransact(code, data, reply, flags);//調(diào)用onTransact()函數(shù)
} catch (RemoteException e) {
if ((flags & FLAG_ONEWAY) != 0) {
Log.w(TAG, "Binder call failed.", e);
} else {
reply.setDataPosition(0);
reply.writeException(e);
}
res = true;
}
...
return res;
}
}
final class BinderProxy implements IBinder {
...
public IInterface queryLocalInterface(String descriptor) {
return null;
}
public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
Binder.checkParcel(this, code, data, "Unreasonably large binder buffer");
return transactNative(code, data, reply, flags);
}
public native String getInterfaceDescriptor() throws RemoteException;
public native boolean transactNative(int code, Parcel data, Parcel reply,
int flags) throws RemoteException;
public native void linkToDeath(DeathRecipient recipient, int flags)
throws RemoteException;
public native boolean unlinkToDeath(DeathRecipient recipient, int flags);
BinderProxy() {
mSelf = new WeakReference(this);
}
...
}
最后,在來看IInterface接口,很簡單,就一個方法,返回值是IBinder。
public interface IInterface
{
public IBinder asBinder();
}
四者的UML類圖如下所示:
這就是BInder機制Proxy-Stub模式原始配方:
在這個類圖的最頂層,有兩個接口,IInterface和IBinder。IBinder代表跨進程傳輸?shù)哪芰?,而IInterface則用來輔助實現(xiàn)具體的傳輸業(yè)務(wù)。Binder是IBinder的實現(xiàn)類,因此它具備跨進程傳輸?shù)哪芰?,它實際上就是遠程Server端的Binder對象本身。與此對應(yīng)的BinderProxy則是遠程Binder的代理對象,給Client進程用的。在跨越進程的時候,Binder驅(qū)動會自動完成這兩個對象的轉(zhuǎn)換。
當(dāng)我們寫了一個AIDL文件之后,如下:
interface ICompute {
int add(int a,int b);
}
此時,會生成一個相應(yīng)的java文件,如下:
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: D:\\Code\\Github\\Android\\Demo\\src\\com\\tfygg\\demo\\service\\ICompute.aidl
*/
package com.tfygg.demo.service;
public interface ICompute extends android.os.IInterface {
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.tfygg.demo.service.ICompute {
private static final java.lang.String DESCRIPTOR = "com.tfygg.demo.service.ICompute";
/** Construct the stub at attach it to the interface. */
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.tfygg.demo.service.ICompute
* interface, generating a proxy if needed.
*/
public static com.tfygg.demo.service.ICompute asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.tfygg.demo.service.ICompute))) {
return ((com.tfygg.demo.service.ICompute) iin);
}
return new com.tfygg.demo.service.ICompute.Stub.Proxy(obj);
}
@Override
public android.os.IBinder asBinder() {
return this;
}
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)
throws android.os.RemoteException {
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_add: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
int _result = this.add(_arg0, _arg1);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.tfygg.demo.service.ICompute {
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
@Override
public android.os.IBinder asBinder() {
return mRemote;
}
public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}
@Override
public int add(int a, int b) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(a);
_data.writeInt(b);
mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
public int add(int a, int b) throws android.os.RemoteException;
}
對照著生成出來的ICompute.java文件,繪制如下Binder模型圖:
上圖中綠色部分就是生成的java文件的內(nèi)容,我們發(fā)現(xiàn)AIDL的Binder模型是基于原始配方的擴展。當(dāng)我們寫完ICompute.aidl之后,ICompute,Stub,Proxy已經(jīng)自動生成出來,其作用如下:
(1)ICompute接口繼承了IInterface,并寫了add(a,b)的方法,代表Server端進程具備計算兩數(shù)相加的能力。此方法將在Stub的具體實現(xiàn)類中重寫。
(2)Stub是一個抽象類,他本質(zhì)是一個Binder,他存在的目的之一是約定好asInterface,asBinder,onTransact方法,減少我們開發(fā)具體Binder對象的工作量。此外,Stub即需要跨進程傳輸,又需要約定遠端服務(wù)端具備的能力,因此他需要同時實現(xiàn)IInterface和IBinder接口,通過上文的類圖可以看得出來。
(3)Proxy是代理對象,它在Client進程中使用,它持有BinderProxy對象,BinderProxy能幫我們訪問服務(wù)端Binder對象的能力。
5.2 C空間
在C空間中,仍然是一樣的Binder配方,不同的是,C空間沒有了AIDL,而是使用模板輔助實現(xiàn)了Proxy-stub。所以,在C空間中也有IBinder.h,Binder。UML類圖如下:
先看IBinder,其定義截選如下:
class IBinder : public virtual RefBase
{
public:
. . . . . .
IBinder();
virtual sp<IInterface> queryLocalInterface(const String16& descriptor);
virtual const String16& getInterfaceDescriptor() const = 0;
virtual bool isBinderAlive() const = 0;
virtual status_t pingBinder() = 0;
virtual status_t dump(int fd, const Vector<String16>& args) = 0;
virtual status_t transact(uint32_t code, const Parcel& data,
Parcel* reply, uint32_t flags = 0) = 0;
class DeathRecipient : public virtual RefBase
{
public:
virtual void binderDied(const wp<IBinder>& who) = 0;
};
virtual status_t linkToDeath(const sp<DeathRecipient>& recipient,
void* cookie = NULL, uint32_t flags = 0) = 0;
virtual status_t unlinkToDeath(const wp<DeathRecipient>& recipient,
void* cookie = NULL, uint32_t flags = 0,
wp<DeathRecipient>* outRecipient = NULL) = 0;
virtual bool checkSubclass(const void* subclassID) const;
typedef void (*object_cleanup_func)(const void* id, void* obj, void* cleanupCookie);
virtual void attachObject(const void* objectID, void* object,
void* cleanupCookie, object_cleanup_func func) = 0;
virtual void* findObject(const void* objectID) const = 0;
virtual void detachObject(const void* objectID) = 0;
virtual BBinder* localBinder();
virtual BpBinder* remoteBinder();
protected:
virtual ~IBinder();
private:
};
有接口必然有實現(xiàn),BBinder和BpBinder都是IBinder的實現(xiàn)類,這里就總結(jié)一下他們的區(qū)別:
(1)pingBinder, BBinder直接返回OK,而BpBinder需要運行一個transact函數(shù),這個函數(shù)很重要。
(2)linkToDeath()是用來在服務(wù)掛的時候通知客戶端的,那服務(wù)端當(dāng)然不需要自己監(jiān)視自己咯,所以BBinder直接返回非法,而Bpbinder需要通過requestDeathNotification()要求某人完成這個事情。
奇怪的是BBinder和BpBinder都沒有實現(xiàn)queryLocalInterface() 接口啊,那肯定另有他人實現(xiàn)這個類了,這個人就是IInterface.h??蛻舫绦蛲ㄟ^queryLocalInterface() 可以知道服務(wù)端都提供哪些服務(wù)。
在IInterface.h中定義的BnInterface和BpInterface是兩個重要的模版,這是為各種程序中使用的。
template
class BnInterface : public INTERFACE, public BBinder
{
public:
virtual sp queryLocalInterface(const String16& _descriptor);
virtual String16 getInterfaceDescriptor() const;
protected:
virtual IBinder* onAsBinder();
};
BnInterface模版的定義如下所示:
template
class BpInterface : public INTERFACE, public BpRefBase
{
public:
BpInterface(const sp& remote);
protected:
virtual IBinder* onAsBinder();
};
這兩個模版在使用的時候,起到得作用實際上都是雙繼承:使用者定義一個接口INTERFACE,然后使用BnInterface和BpInterface兩個模版結(jié)合自己的接口,構(gòu)建自己的BnXXX和BpXXX兩個類。
DECLARE_META_INTERFACE和IMPLEMENT_META_INTERFACE兩個宏用于幫助BpXXX類的實現(xiàn):
#define DECLARE_META_INTERFACE(INTERFACE) /
static const String16 descriptor; /
static sp asInterface(const sp& obj); /
virtual String16 getInterfaceDescriptor() const; /
#define IMPLEMENT_META_INTERFACE(INTERFACE, NAME) /
const String16 I##INTERFACE::descriptor(NAME); /
String16 I##INTERFACE::getInterfaceDescriptor() const { /
return I##INTERFACE::descriptor; /
} /
sp I##INTERFACE::asInterface(const sp& obj) /
{ /
sp intr; /
if (obj != NULL) { /
intr = static_cast( /
obj->queryLocalInterface( /
I##INTERFACE::descriptor).get()); /
if (intr == NULL) { /
intr = new Bp##INTERFACE(obj); /
} /
} /
return intr; /
}
在定義自己的類的時候,只需要使用DECLARE_META_INTERFACE和IMPLEMENT_META_INTERFACE兩個接口,并結(jié)合類的名稱,就可以實現(xiàn)BpInterface中asInterface()和getInterfaceDescriptor()兩個函數(shù)。
此外,IInterface還起到了轉(zhuǎn)換的作用,IXXXService繼承自IInterface,而IInterface中的asBinder()方法,會將自身,也就是IXXXService轉(zhuǎn)換成一個IBinder對象,而asInterface接口則是將IIBinder轉(zhuǎn)換為IXXXService接口。
template<typename INTERFACE>
inline sp<INTERFACE> interface_cast(const sp<IBinder>& obj)
{
return INTERFACE::asInterface(obj);
}
由此可知,使用interface_cast的作用就是將IBinder實例轉(zhuǎn)化成IXXXService實例。
可以看出,C空間的IBinder接口和java空間類似,下圖可以看出C空間的binder的Proxy-stub
部分摘自
https://blog.csdn.net/augfun/article/details/82343249
https://blog.csdn.net/AndroidStudyDay/article/details/93749470
https://blog.csdn.net/tfygg/article/details/51626632