Android Binder 通信原理圖文詳解
前言
Binder機(jī)制可謂是Android 知識(shí)體系里的重中之重,作為偏底層的基礎(chǔ)組件,平時(shí)我們很少關(guān)注它,而它卻是無處不在,也是Android 面試易考察的點(diǎn)之一。網(wǎng)上很多文章,要么知識(shí)點(diǎn)比較陳舊,要么源碼貼一堆,要么沒有成體系地分析,導(dǎo)致讀者一知半解,似是而非。
本篇將從流程上將Binder通信過一遍,盡量多用圖展示。
通過本篇文章,你將了解到:
Binder的作用
進(jìn)程與Binder驅(qū)動(dòng)如何通信
ServiceManager進(jìn)程的作用
進(jìn)程添加服務(wù)到ServiceManager的流程
進(jìn)程從ServiceManager獲取服務(wù)的流程
Binder服務(wù)端數(shù)據(jù)接收
Binder 通信全流程圖
1. Binder的作用
先看Linux下進(jìn)程地址映射關(guān)系:

我們知道,對象調(diào)用本身就是地址空間的訪問。
如上,進(jìn)程之間各自訪問各自的內(nèi)存地址,它們之間無法直接訪問對方的地址,也就是說微信不能直接調(diào)用支付寶提供的接口。而內(nèi)核具有訪問其它進(jìn)程地址空間的權(quán)限,因此微信可以將消息發(fā)送給內(nèi)核,讓內(nèi)核幫忙轉(zhuǎn)發(fā)給支付寶,這種方式叫做:存儲(chǔ)/轉(zhuǎn)發(fā)方式。
由此衍生的幾種IPC(進(jìn)程間通信)如:管道、消息隊(duì)列、socket等,而Android 上采用了新的機(jī)制:
Binder,相比傳統(tǒng)的方式,Binder只需要一次數(shù)據(jù)拷貝,并且Binder更安全。
Binder機(jī)制是Android 里用來做IPC的主要方式。
2. 進(jìn)程與Binder驅(qū)動(dòng)如何通信
既然得要內(nèi)核進(jìn)行消息中轉(zhuǎn),那么Binder驅(qū)動(dòng)得運(yùn)行在內(nèi)核空間,而事實(shí)上也確實(shí)如此,Binder驅(qū)動(dòng)加載后在內(nèi)核空間運(yùn)行,進(jìn)程只需要和Binder驅(qū)動(dòng)取得聯(lián)系,通過Binder驅(qū)動(dòng)聯(lián)系另一個(gè)進(jìn)程,那么一次消息的傳送過程就可以實(shí)現(xiàn)了。

內(nèi)核提供提供一系列的系統(tǒng)調(diào)用接口給用戶進(jìn)程使用,當(dāng)用戶進(jìn)程想要訪問內(nèi)核時(shí),只需要調(diào)用對應(yīng)的接口,此時(shí)代碼就會(huì)從用戶空間切換到內(nèi)核空間執(zhí)行。
常見的系統(tǒng)調(diào)用函數(shù)如:open/read/write/ioctl/close/mmap/fork 等。
與Binder驅(qū)動(dòng)通信分兩步:
打開Binder驅(qū)動(dòng):open("/dev/binder", O_RDWR | O_CLOEXEC)
通過ioctl 與Binder驅(qū)動(dòng)進(jìn)行數(shù)據(jù)通信:ioctl(mDriverFD, BINDER_WRITE_READ, &bwr)
bwr 為讀寫數(shù)據(jù)結(jié)構(gòu)
3. ServiceManager進(jìn)程的作用
Binder Client、Binder Server、ServiceManager關(guān)系
為方便起見,ServiceManager簡稱SM。
Binder 設(shè)計(jì)為C/S架構(gòu),C為Client(客戶端),S為Server(服務(wù)端),Server端提供接口(服務(wù))給Client端使用,而這個(gè)服務(wù)是以Binder引用的形式提供的。
由之前的知識(shí)可知,C和S是不同的進(jìn)程,那么C如何拿到S的Binder引用呢?
你可能會(huì)說,當(dāng)然是SM了,S先將Binder引用存放在SM里,當(dāng)C需要的時(shí)候向SM查詢即可。
這么看似乎講得通了,那問題又來了,SM也是一個(gè)單獨(dú)的進(jìn)程,那S、C如何與SM進(jìn)行通信呢?這就陷入了先有雞還是先有蛋的死循環(huán)了。
實(shí)際上C、S、SM之間都是依靠Binder通信,只是SM作為特殊的Binder(handle=0)提前放入了Binder驅(qū)動(dòng)里,當(dāng)C、S想要獲取SM的Binder引用,只需要獲取handle=0的Binder即可。
這么說沒有太直觀的印象,我們一步步剖析。
ServiceManager注冊進(jìn)Binder

SM 注冊進(jìn)Binder驅(qū)動(dòng)后就會(huì)等待來自Binder驅(qū)動(dòng)的消息,這里列出了兩個(gè)最常見的處理消息的Case:
其它進(jìn)程添加服務(wù)到SM里
其它進(jìn)程向SM查詢服務(wù)
SM里維護(hù)著一個(gè)鏈表,鏈表的元素是結(jié)構(gòu)體:

主要記錄的是name和handle字段。
當(dāng)SM收到添加服務(wù)的指令后,從Binder驅(qū)動(dòng)里取出handle和name,并構(gòu)造結(jié)構(gòu)體插入到鏈表。
當(dāng)SM收到查詢服務(wù)的指令后,從Binder驅(qū)動(dòng)里取出name,并找到鏈表里相同的name,找到后取出handle,最后寫入到Binder驅(qū)動(dòng)。
4. 進(jìn)程添加服務(wù)到ServiceManager的流程
其它進(jìn)程找到SM
現(xiàn)在SM已經(jīng)翹首以盼其它進(jìn)程的請求了,接著來看看如何添加一個(gè)服務(wù)到SM里。
以Java層添加服務(wù)為例,我們選擇振動(dòng)服務(wù)作為切入點(diǎn)分析。

在system_server 進(jìn)程里構(gòu)造振動(dòng)服務(wù)(VibratorService繼承自Binder),并添加到SM里。

可以看出,分兩步:
先找到ServiceManager
往ServiceManager里添加服務(wù)
getIServiceManager()繼續(xù)往下:

BinderInternal.getContextObject() 是native方法,后續(xù)流程較多,我們用圖表示。

尋找ServiceManager的過程涉及到Java層和Native層,主要的重點(diǎn)在Native層查找 ServiceManager對應(yīng)的BpBinder對象,沒有找到的話則創(chuàng)建新的并存入緩存里以備下次直接獲取。
ProcessState里維護(hù)了一個(gè)單例,每個(gè)進(jìn)程只有一個(gè)ProcessState對象,創(chuàng)建ProcessState時(shí)候就會(huì)去打開Binder驅(qū)動(dòng),同時(shí)會(huì)設(shè)置Binder線程池里線程個(gè)數(shù)等其它參數(shù)
Native層構(gòu)造BpBinder(handle=0表示該BpBinder是ServiceManager在客戶端的引用),再構(gòu)造BinderProxyNativeData持有BpBinder。
構(gòu)造BinderProxy對象并持有BinderProxyNativeData,也就是間接持有BpBinder
最后構(gòu)造了ServiceManagerProxy對象,它實(shí)現(xiàn)了IServiceManager接口,它的成員變量mRemote指向了BinderProxy
可以看出,獲取ServiceManager的過程并不是真正去獲取ServiceManager的Binder對象,而是獲取它在當(dāng)前進(jìn)程的代理:BpBinder
添加服務(wù)到ServiceManager
既然找到了SM的Binder代理,接下來看看如何使用它給SM添加服務(wù)。
#ServiceManagerNative.ServiceManagerProxy
public void addService(String name, IBinder service, boolean allowIsolated, int dumpPriority)
throws RemoteException {
//構(gòu)造Parcel
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IServiceManager.descriptor);
data.writeString(name);
//寫入Binder
data.writeStrongBinder(service);
data.writeInt(allowIsolated ? 1 : 0);
data.writeInt(dumpPriority);
//通過BinderProxy發(fā)送
mRemote.transact(ADD_SERVICE_TRANSACTION, data, reply, 0);
reply.recycle();
data.recycle();
}

其中IPCThreadState與線程相關(guān),不同的線程會(huì)維護(hù)一個(gè)單例。
由此可見,最終還是通過BpBinder發(fā)送消息,進(jìn)而發(fā)送到Binder驅(qū)動(dòng)。
此時(shí)驅(qū)動(dòng)收到的信息包括不限于:
服務(wù)的名字
ServiceManager的handle
BBinder對象指針
驅(qū)動(dòng)建立服務(wù)handle和BBinder對象指針的映射關(guān)系,并將服務(wù)的名字和服務(wù)的handle傳遞給ServiceManager(通過ServiceManager handle查找)。
ServiceManager拿到消息后建立映射關(guān)系,等待其它進(jìn)程的請求。
至此,進(jìn)程添加服務(wù)到ServiceManager過程已經(jīng)分析完畢,用圖表示如下:

BBinder作用
Java層傳遞的是Binder對象,如何與Native的BBinder關(guān)聯(lián)起來呢?
重點(diǎn)在:
Parcel.writeStrongBinder(Binder)

也即是說Server端的Java Binder對象在Native層的代表是BBinder。
Binder驅(qū)動(dòng)記錄了BBinder的地址,當(dāng)有消息過來時(shí)通過找到BBinder對象進(jìn)而找到Java層的Binder對象,最終調(diào)用Binder.onTransact()。
5. 進(jìn)程從ServiceManager獲取服務(wù)的流程
其它進(jìn)程找到SM
振動(dòng)服務(wù)添加完成后,某些進(jìn)程想要獲取振動(dòng)服務(wù)進(jìn)行振動(dòng),比如微信收到消息后需要振動(dòng)用以提示用戶。
接著來看看如何獲取振動(dòng)服務(wù)。
private void vibrate() {
//獲取振動(dòng)服務(wù)
Vibrator vibrator = (Vibrator)getSystemService(Context.VIBRATOR_SERVICE);
//開始振動(dòng)
vibrator.vibrate(1000);
}
與添加服務(wù)類似,想要獲取服務(wù)先要找到SM,找SM的過程上邊分析過了,此處不再細(xì)說。
從ServiceManager獲取服務(wù)
#ServiceManagerNative.ServiceManagerProxy
public IBinder getService(String name) throws RemoteException {
//構(gòu)造Parcel
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IServiceManager.descriptor);
//寫入名字
data.writeString(name);
//通過BinderProxy發(fā)送
mRemote.transact(GET_SERVICE_TRANSACTION, data, reply, 0);
IBinder binder = reply.readStrongBinder();
reply.recycle();
data.recycle();
return binder;
}

由此可見,最終還是通過BpBinder發(fā)送消息,進(jìn)而發(fā)送到Binder驅(qū)動(dòng)。
此時(shí)驅(qū)動(dòng)收到的信息包括不限于:
服務(wù)的名字
ServiceManager的handle
Binder驅(qū)動(dòng)收到消息后,找到SM,并將服務(wù)的名字傳給SM,SM從自己維護(hù)的鏈表里找到服務(wù)名相同的節(jié)點(diǎn),最終取出該服務(wù)的handle,發(fā)送給Binder驅(qū)動(dòng)。
用圖表示如下:

對比添加服務(wù)流程和獲取服務(wù)流程,兩者前半部分都很相似,都是先拿到SM的BpBinder引用,然后寫入驅(qū)動(dòng),最后由SM進(jìn)程處理。只是對于獲取服務(wù)流程來說,還需要將查詢的結(jié)果(handle)寫入驅(qū)動(dòng)返回給調(diào)用方(對應(yīng)圖上紅色部分)。
到這,大家可能會(huì)有疑惑了:"handle是整形值,而微信獲取的振動(dòng)服務(wù)是一個(gè)Binder對象,這兩者是怎么結(jié)合起來的呢?"
handle轉(zhuǎn)換為Binder對象
handle表示的即是Binder服務(wù)端在客戶端的索引句柄,只要客戶端拿到了handle,它就能通過Binder驅(qū)動(dòng)調(diào)用到服務(wù)端。
mRemote.transact(GET_SERVICE_TRANSACTION, data, reply, 0);
IBinder binder = reply.readStrongBinder();
再回過頭看看獲取服務(wù)的代碼,當(dāng)微信進(jìn)程將查詢命令發(fā)給Binder驅(qū)動(dòng)后就等待驅(qū)動(dòng)回復(fù)的結(jié)果,SM查詢到結(jié)果后將handle寫入驅(qū)動(dòng),而后微信進(jìn)程從驅(qū)動(dòng)將結(jié)果讀出并將結(jié)果存入reply字段。
最后通過reply拿到Binder引用,也就是說重點(diǎn)在reply.readStrongBinder()方法。
直接看圖:

如上,通過驅(qū)動(dòng)返回的handle構(gòu)造BpBinder,最終封裝為Java層的BinderProxy。
至此,獲取服務(wù)流程就結(jié)束了,用圖展示簡化的流程

6. Binder服務(wù)端數(shù)據(jù)接收
微信進(jìn)程拿到振動(dòng)服務(wù)(在system_server進(jìn)程里)的Binder(BinderProxy)后,就可以調(diào)用振動(dòng)方法了,而后指令發(fā)送給驅(qū)動(dòng),驅(qū)動(dòng)通過振動(dòng)服務(wù)的handle找到對應(yīng)的服務(wù)BBinder指針,從而調(diào)用服務(wù)的接收方法。
微信進(jìn)程發(fā)送指令給Binder驅(qū)動(dòng)前面已經(jīng)分析過,重點(diǎn)來看看system_server進(jìn)程是如何接收并處理指令的。

system_server進(jìn)程啟動(dòng)的時(shí)候就會(huì)開啟Binder線程池,并等待驅(qū)動(dòng)數(shù)據(jù)到來。
當(dāng)system_server進(jìn)程添加振動(dòng)服務(wù)到SM時(shí),會(huì)將Java層的Binder轉(zhuǎn)為Native層的BBinder,并將BBinder對象指針寫入Binder驅(qū)動(dòng)。
當(dāng)微信進(jìn)程調(diào)用system_server接口時(shí):
微信進(jìn)程調(diào)用BpBinder.transact()將handle和數(shù)據(jù)寫入Binder驅(qū)動(dòng)
Binder驅(qū)動(dòng)根據(jù)handle找到system_server進(jìn)程
system_server進(jìn)程從驅(qū)動(dòng)拿到數(shù)據(jù),并取出BBinder指針,最終調(diào)用到system_server進(jìn)程Java層的Binder.onTransact()
如此一來,微信成功調(diào)用了振動(dòng)服務(wù),也就是說一次Client到Server端的通信就完成了。
7. Binder 通信全流程圖
縱觀Binder機(jī)制設(shè)計(jì),最核心的點(diǎn)是handle。
通過handle構(gòu)造Client端的BpBinder(Native層),與此對應(yīng)的是Java層的BinderProxy
通過handle,驅(qū)動(dòng)找到Server端進(jìn)程,進(jìn)而調(diào)用BBinder(Native層),與此對應(yīng)的是Java層的Binder
通過handle的一系列中轉(zhuǎn),Client.transact()成功調(diào)用了Server.onTransact(),一次Binder通信就過程就完成了
最后,用一張圖總結(jié)Binder機(jī)制的全過程:

以上就是整個(gè)Binder機(jī)制的梳理過程,此間省略了Binder驅(qū)動(dòng)里的映射邏輯,可以將Binder驅(qū)動(dòng)當(dāng)做一個(gè)黑盒,而更重要的是Binder客戶端和服務(wù)端是如何進(jìn)行映射的。
Binder流程比較繞,尤其是IPCThreadStsate作為客戶端的發(fā)送和服務(wù)端的數(shù)據(jù)接收的實(shí)體,需要區(qū)分不同的場景。
當(dāng)然,jni基礎(chǔ)知識(shí)必不可少。
本文基于Android 10
以上就是Android Binder 通信原理圖文詳解的詳細(xì)內(nèi)容,更多關(guān)于Android Binder 通信原理的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android即時(shí)通訊設(shè)計(jì)(騰訊IM接入和WebSocket接入)
本文主要介紹了Android即時(shí)通訊設(shè)計(jì)(騰訊IM接入和WebSocket接入),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-04-04
Android開發(fā)快速實(shí)現(xiàn)底部導(dǎo)航欄示例
這篇文章主要為大家介紹了Android開發(fā)快速實(shí)現(xiàn)底部導(dǎo)航欄的示例代碼,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-04-04
Android智能指針輕量級(jí)Light Pointer初識(shí)
這篇文章主要為大家介紹了Android智能指針輕量級(jí)Light Pointer初識(shí)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12
android 引導(dǎo)界面的實(shí)現(xiàn)方法
現(xiàn)在越來越多程序都有引導(dǎo)頁面了。網(wǎng)上資料不全?,F(xiàn)在自己實(shí)現(xiàn)下。2013-06-06
Android開發(fā)AsmClassVisitorFactory使用詳解
這篇文章主要為大家介紹了Android開發(fā)AsmClassVisitorFactory使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-06-06
簡單介紹Android開發(fā)中的Activity控件的基本概念
這篇文章主要介紹了Android開發(fā)中的Activity控件的基本概念,Activity控件的使用是安卓開發(fā)的基礎(chǔ)之一,需要的朋友可以參考下2015-12-12
搭建mac使用Charles抓包安卓app環(huán)境配置過程
這篇文章主要為大家介紹了mac使用Charles抓包,安卓app環(huán)境搭建的配置過程步驟,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步2022-02-02
Android Handler的postDelayed()關(guān)閉的方法及遇到問題
這篇文章主要介紹了Android Handler的postDelayed()關(guān)閉的方法,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-04-04
Android實(shí)現(xiàn)幀動(dòng)畫的兩種方式
幀動(dòng)畫(Frame?Animation)是一種在一定時(shí)間內(nèi)按順序播放一系列圖像幀(每一幀都是一個(gè)單獨(dú)的圖像),從而產(chǎn)生連續(xù)運(yùn)動(dòng)或變化的動(dòng)畫效果,本文給大家介紹了Android實(shí)現(xiàn)幀動(dòng)畫的兩種方式,需要的朋友可以參考下2024-02-02
Android 代碼設(shè)置開機(jī)自啟動(dòng)App的方法
今天小編就為大家分享一篇Android 代碼設(shè)置開機(jī)自啟動(dòng)App的方法,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-07-07

