前言
IPC 系列文章:
建議按順序閱讀榜旦。
Android IPC 之Service 還可以這么理解
Android IPC 之Binder基礎(chǔ)
Android IPC 之Binder應(yīng)用
Android IPC 之AIDL應(yīng)用(上)
Android IPC 之AIDL應(yīng)用(下)
Android IPC 之Messenger 原理及應(yīng)用
Android IPC 之服務(wù)端回調(diào)
Android IPC 之獲取服務(wù)(IBinder)
Android Binder 原理換個姿勢就頓悟了(圖文版)
Binder機制可謂是Android 知識體系里的重中之重幽七,作為偏底層的基礎(chǔ)組件,平時我們很少關(guān)注它溅呢,而它卻是無處不在澡屡,也是Android 面試易考察的點之一。網(wǎng)上很多文章咐旧,要么知識點比較陳舊驶鹉,要么源碼貼一堆,要么沒有成體系地分析铣墨,導致讀者一知半解室埋,似是而非。
本篇將從流程上將Binder通信過一遍伊约,盡量多用圖展示姚淆。
通過本篇文章,你將了解到:
- Binder的作用
- 進程與Binder驅(qū)動如何通信
- ServiceManager進程的作用
- 進程添加服務(wù)到ServiceManager的流程
- 進程從ServiceManager獲取服務(wù)的流程
- Binder服務(wù)端數(shù)據(jù)接收
- Binder 通信全流程圖
1. Binder的作用
先看Linux下進程地址映射關(guān)系:
我們知道屡律,對象調(diào)用本身就是地址空間的訪問腌逢。
如上,進程之間各自訪問各自的內(nèi)存地址超埋,它們之間無法直接訪問對方的地址搏讶,也就是說微信不能直接調(diào)用支付寶提供的接口。而內(nèi)核具有訪問其它進程地址空間的權(quán)限霍殴,因此微信可以將消息發(fā)送給內(nèi)核窍蓝,讓內(nèi)核幫忙轉(zhuǎn)發(fā)給支付寶,這種方式叫做:存儲/轉(zhuǎn)發(fā)方式繁成。
由此衍生的幾種IPC(進程間通信)如:管道、消息隊列淑玫、socket等巾腕,而Android 上采用了新的機制:Binder面睛,相比傳統(tǒng)的方式,Binder只需要一次數(shù)據(jù)拷貝尊搬,并且Binder更安全叁鉴。
Binder機制是Android 里用來做IPC的主要方式。
2. 進程與Binder驅(qū)動如何通信
既然得要內(nèi)核進行消息中轉(zhuǎn)佛寿,那么Binder驅(qū)動得運行在內(nèi)核空間幌墓,而事實上也確實如此,Binder驅(qū)動加載后在內(nèi)核空間運行冀泻,進程只需要和Binder驅(qū)動取得聯(lián)系常侣,通過Binder驅(qū)動聯(lián)系另一個進程,那么一次消息的傳送過程就可以實現(xiàn)了弹渔。
內(nèi)核提供提供一系列的系統(tǒng)調(diào)用接口給用戶進程使用胳施,當用戶進程想要訪問內(nèi)核時,只需要調(diào)用對應(yīng)的接口肢专,此時代碼就會從用戶空間切換到內(nèi)核空間執(zhí)行舞肆。
常見的系統(tǒng)調(diào)用函數(shù)如:open/read/write/ioctl/close/mmap/fork 等。
與Binder驅(qū)動通信分兩步:
- 打開Binder驅(qū)動:open("/dev/binder", O_RDWR | O_CLOEXEC)
- 通過ioctl 與Binder驅(qū)動進行數(shù)據(jù)通信:ioctl(mDriverFD, BINDER_WRITE_READ, &bwr)
bwr 為讀寫數(shù)據(jù)結(jié)構(gòu)
3. ServiceManager進程的作用
Binder Client博杖、Binder Server椿胯、ServiceManager關(guān)系
為方便起見,ServiceManager簡稱SM剃根。
Binder 設(shè)計為C/S架構(gòu)哩盲,C為Client(客戶端),S為Server(服務(wù)端)跟继,Server端提供接口(服務(wù))給Client端使用种冬,而這個服務(wù)是以Binder引用的形式提供的。
由之前的知識可知舔糖,C和S是不同的進程娱两,那么C如何拿到S的Binder引用呢?
你可能會說金吗,當然是SM了十兢,S先將Binder引用存放在SM里,當C需要的時候向SM查詢即可摇庙。
這么看似乎講得通了旱物,那問題又來了,SM也是一個單獨的進程卫袒,那S宵呛、C如何與SM進行通信呢?這就陷入了先有雞還是先有蛋的死循環(huán)了夕凝。
實際上C宝穗、S户秤、SM之間都是依靠Binder通信,只是SM作為特殊的Binder(handle=0)提前放入了Binder驅(qū)動里逮矛,當C鸡号、S想要獲取SM的Binder引用,只需要獲取handle=0的Binder即可须鼎。
這么說沒有太直觀的印象鲸伴,我們一步步剖析。
ServiceManager注冊進Binder
SM 注冊進Binder驅(qū)動后就會等待來自Binder驅(qū)動的消息晋控,這里列出了兩個最常見的處理消息的Case:
- 其它進程添加服務(wù)到SM里
- 其它進程向SM查詢服務(wù)
SM里維護著一個鏈表汞窗,鏈表的元素是結(jié)構(gòu)體:
主要記錄的是name和handle字段。
當SM收到添加服務(wù)的指令后糖荒,從Binder驅(qū)動里取出handle和name杉辙,并構(gòu)造結(jié)構(gòu)體插入到鏈表。
當SM收到查詢服務(wù)的指令后捶朵,從Binder驅(qū)動里取出name蜘矢,并找到鏈表里相同的name,找到后取出handle综看,最后寫入到Binder驅(qū)動品腹。
4. 進程添加服務(wù)到ServiceManager的流程
其它進程找到SM
現(xiàn)在SM已經(jīng)翹首以盼其它進程的請求了,接著來看看如何添加一個服務(wù)到SM里红碑。
以Java層添加服務(wù)為例舞吭,我們選擇振動服務(wù)作為切入點分析。
在system_server 進程里構(gòu)造振動服務(wù)(VibratorService繼承自Binder)析珊,并添加到SM里羡鸥。
可以看出,分兩步:
- 先找到ServiceManager
- 往ServiceManager里添加服務(wù)
getIServiceManager()繼續(xù)往下:
BinderInternal.getContextObject() 是native方法忠寻,后續(xù)流程較多惧浴,我們用圖表示。
尋找ServiceManager的過程涉及到Java層和Native層奕剃,主要的重點在Native層查找ServiceManager對應(yīng)的BpBinder對象衷旅,沒有找到的話則創(chuàng)建新的并存入緩存里以備下次直接獲取。
- ProcessState里維護了一個單例纵朋,每個進程只有一個ProcessState對象柿顶,創(chuàng)建ProcessState時候就會去打開Binder驅(qū)動,同時會設(shè)置Binder線程池里線程個數(shù)等其它參數(shù)
- Native層構(gòu)造BpBinder(handle=0表示該BpBinder是ServiceManager在客戶端的引用)操软,再構(gòu)造BinderProxyNativeData持有BpBinder嘁锯。
- 構(gòu)造BinderProxy對象并持有BinderProxyNativeData,也就是間接持有BpBinder
- 最后構(gòu)造了ServiceManagerProxy對象,它實現(xiàn)了IServiceManager接口猪钮,它的成員變量mRemote指向了BinderProxy
可以看出品山,獲取ServiceManager的過程并不是真正去獲取ServiceManager的Binder對象,而是獲取它在當前進程的代理: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)笆载,不同的線程會維護一個單例扑馁。
由此可見,最終還是通過BpBinder發(fā)送消息凉驻,進而發(fā)送到Binder驅(qū)動腻要。
此時驅(qū)動收到的信息包括不限于:
- 服務(wù)的名字
- ServiceManager的handle
- BBinder對象指針
驅(qū)動建立服務(wù)handle和BBinder對象指針的映射關(guān)系,并將服務(wù)的名字和服務(wù)的handle傳遞給ServiceManager(通過ServiceManager handle查找)涝登。
ServiceManager拿到消息后建立映射關(guān)系雄家,等待其它進程的請求。
至此胀滚,進程添加服務(wù)到ServiceManager過程已經(jīng)分析完畢趟济,用圖表示如下:
BBinder作用
Java層傳遞的是Binder對象,如何與Native的BBinder關(guān)聯(lián)起來呢咽笼?
重點在:
Parcel.writeStrongBinder(Binder)
也即是說Server端的Java Binder對象在Native層的代表是BBinder顷编。
Binder驅(qū)動記錄了BBinder的地址,當有消息過來時通過找到BBinder對象進而找到Java層的Binder對象剑刑,最終調(diào)用Binder.onTransact()媳纬。
5. 進程從ServiceManager獲取服務(wù)的流程
其它進程找到SM
振動服務(wù)添加完成后,某些進程想要獲取振動服務(wù)進行振動施掏,比如微信收到消息后需要振動用以提示用戶钮惠。
接著來看看如何獲取振動服務(wù)。
private void vibrate() {
//獲取振動服務(wù)
Vibrator vibrator = (Vibrator)getSystemService(Context.VIBRATOR_SERVICE);
//開始振動
vibrator.vibrate(1000);
}
與添加服務(wù)類似七芭,想要獲取服務(wù)先要找到SM素挽,找SM的過程上邊分析過了,此處不再細說抖苦。
從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ā)送消息,進而發(fā)送到Binder驅(qū)動锌历。
此時驅(qū)動收到的信息包括不限于:
- 服務(wù)的名字
- ServiceManager的handle
Binder驅(qū)動收到消息后贮庞,找到SM,并將服務(wù)的名字傳給SM究西,SM從自己維護的鏈表里找到服務(wù)名相同的節(jié)點窗慎,最終取出該服務(wù)的handle,發(fā)送給Binder驅(qū)動。
用圖表示如下:
對比添加服務(wù)流程和獲取服務(wù)流程遮斥,兩者前半部分都很相似峦失,都是先拿到SM的BpBinder引用,然后寫入驅(qū)動术吗,最后由SM進程處理尉辑。只是對于獲取服務(wù)流程來說,還需要將查詢的結(jié)果(handle)寫入驅(qū)動返回給調(diào)用方(對應(yīng)圖上紅色部分)较屿。
到這隧魄,大家可能會有疑惑了:"handle是整形值,而微信獲取的振動服務(wù)是一個Binder對象隘蝎,這兩者是怎么結(jié)合起來的呢购啄?"
handle轉(zhuǎn)換為Binder對象
handle表示的即是Binder服務(wù)端在客戶端的索引句柄,只要客戶端拿到了handle嘱么,它就能通過Binder驅(qū)動調(diào)用到服務(wù)端狮含。
mRemote.transact(GET_SERVICE_TRANSACTION, data, reply, 0);
IBinder binder = reply.readStrongBinder();
再回過頭看看獲取服務(wù)的代碼,當微信進程將查詢命令發(fā)給Binder驅(qū)動后就等待驅(qū)動回復的結(jié)果曼振,SM查詢到結(jié)果后將handle寫入驅(qū)動几迄,而后微信進程從驅(qū)動將結(jié)果讀出并將結(jié)果存入reply字段。
最后通過reply拿到Binder引用拴测,也就是說重點在reply.readStrongBinder()方法乓旗。
直接看圖:
如上,通過驅(qū)動返回的handle構(gòu)造BpBinder集索,最終封裝為Java層的BinderProxy屿愚。
至此,獲取服務(wù)流程就結(jié)束了务荆,用圖展示簡化的流程
6. Binder服務(wù)端數(shù)據(jù)接收
微信進程拿到振動服務(wù)(在system_server進程里)的Binder(BinderProxy)后妆距,就可以調(diào)用振動方法了,而后指令發(fā)送給驅(qū)動函匕,驅(qū)動通過振動服務(wù)的handle找到對應(yīng)的服務(wù)BBinder指針娱据,從而調(diào)用服務(wù)的接收方法。
微信進程發(fā)送指令給Binder驅(qū)動前面已經(jīng)分析過盅惜,重點來看看system_server進程是如何接收并處理指令的中剩。
system_server進程啟動的時候就會開啟Binder線程池,并等待驅(qū)動數(shù)據(jù)到來抒寂。
當system_server進程添加振動服務(wù)到SM時结啼,會將Java層的Binder轉(zhuǎn)為Native層的BBinder,并將BBinder對象指針寫入Binder驅(qū)動屈芜。
當微信進程調(diào)用system_server接口時:
- 微信進程調(diào)用BpBinder.transact()將handle和數(shù)據(jù)寫入Binder驅(qū)動
- Binder驅(qū)動根據(jù)handle找到system_server進程
- system_server進程從驅(qū)動拿到數(shù)據(jù)郊愧,并取出BBinder指針朴译,最終調(diào)用到system_server進程Java層的Binder.onTransact()
如此一來,微信成功調(diào)用了振動服務(wù)属铁,也就是說一次Client到Server端的通信就完成了眠寿。
7. Binder 通信全流程圖
縱觀Binder機制設(shè)計,最核心的點是handle焦蘑。
- 通過handle構(gòu)造Client端的BpBinder(Native層)盯拱,與此對應(yīng)的是Java層的BinderProxy
- 通過handle,驅(qū)動找到Server端進程例嘱,進而調(diào)用BBinder(Native層)坟乾,與此對應(yīng)的是Java層的Binder
- 通過handle的一系列中轉(zhuǎn),Client.transact()成功調(diào)用了Server.onTransact()蝶防,一次Binder通信就過程就完成了
最后,用一張圖總結(jié)Binder機制的全過程:
以上就是整個Binder機制的梳理過程明吩,此間省略了Binder驅(qū)動里的映射邏輯间学,可以將Binder驅(qū)動當做一個黑盒,而更重要的是Binder客戶端和服務(wù)端是如何進行映射的印荔。
Binder流程比較繞低葫,尤其是IPCThreadStsate作為客戶端的發(fā)送和服務(wù)端的數(shù)據(jù)接收的實體,需要區(qū)分不同的場景仍律。
當然嘿悬,jni基礎(chǔ)知識必不可少。
本文基于Android 10
限于篇幅并沒有一步步列出源碼水泉,對源碼細節(jié)有疑惑之處歡迎留言討論善涨。
您若喜歡,請點贊草则、關(guān)注钢拧、收藏,您的鼓勵是我前進的動力
持續(xù)更新中炕横,和我一起步步為營系統(tǒng)源内、深入學習Android/Kotlin
1、Android各種Context的前世今生
2份殿、Android DecorView 必知必會
3膜钓、Window/WindowManager 不可不知之事
4、View Measure/Layout/Draw 真明白了
5卿嘲、Android事件分發(fā)全套服務(wù)
6颂斜、Android invalidate/postInvalidate/requestLayout 徹底厘清
7、Android Window 如何確定大小/onMeasure()多次執(zhí)行原因
8腔寡、Android事件驅(qū)動Handler-Message-Looper解析
9焚鲜、Android 鍵盤一招搞定
10、Android 各種坐標徹底明了
11、Android Activity/Window/View 的background
12忿磅、Android Activity創(chuàng)建到View的顯示過
13糯彬、Android IPC 系列
14、Android 存儲系列
15葱她、Java 并發(fā)系列不再疑惑
16撩扒、Java 線程池系列
17、Android Jetpack 前置基礎(chǔ)系列
18吨些、Android Jetpack 易懂易學系列
19搓谆、Kotlin 輕松入門系列
20、Kotlin 協(xié)程系列全面解讀