Android Binder 通信原理圖文詳解
前言
Binder機制可謂是Android 知識體系里的重中之重,作為偏底層的基礎(chǔ)組件,平時我們很少關(guān)注它,而它卻是無處不在,也是Android 面試易考察的點之一。網(wǎng)上很多文章,要么知識點比較陳舊,要么源碼貼一堆,要么沒有成體系地分析,導(dǎo)致讀者一知半解,似是而非。
本篇將從流程上將Binder通信過一遍,盡量多用圖展示。
通過本篇文章,你將了解到:
Binder的作用
進(jìn)程與Binder驅(qū)動如何通信
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ā)給支付寶,這種方式叫做:存儲/轉(zhuǎn)發(fā)方式。
由此衍生的幾種IPC(進(jìn)程間通信)如:管道、消息隊列、socket等,而Android 上采用了新的機制:
Binder,相比傳統(tǒng)的方式,Binder只需要一次數(shù)據(jù)拷貝,并且Binder更安全。
Binder機制是Android 里用來做IPC的主要方式。
2. 進(jìn)程與Binder驅(qū)動如何通信
既然得要內(nèi)核進(jìn)行消息中轉(zhuǎn),那么Binder驅(qū)動得運行在內(nèi)核空間,而事實上也確實如此,Binder驅(qū)動加載后在內(nèi)核空間運行,進(jìn)程只需要和Binder驅(qū)動取得聯(lián)系,通過Binder驅(qū)動聯(lián)系另一個進(jìn)程,那么一次消息的傳送過程就可以實現(xiàn)了。
內(nèi)核提供提供一系列的系統(tǒng)調(diào)用接口給用戶進(jìn)程使用,當(dāng)用戶進(jìn)程想要訪問內(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ū)動進(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è)計為C/S架構(gòu),C為Client(客戶端),S為Server(服務(wù)端),Server端提供接口(服務(wù))給Client端使用,而這個服務(wù)是以Binder引用的形式提供的。
由之前的知識可知,C和S是不同的進(jìn)程,那么C如何拿到S的Binder引用呢?
你可能會說,當(dāng)然是SM了,S先將Binder引用存放在SM里,當(dāng)C需要的時候向SM查詢即可。
這么看似乎講得通了,那問題又來了,SM也是一個單獨的進(jìn)程,那S、C如何與SM進(jìn)行通信呢?這就陷入了先有雞還是先有蛋的死循環(huán)了。
實際上C、S、SM之間都是依靠Binder通信,只是SM作為特殊的Binder(handle=0)提前放入了Binder驅(qū)動里,當(dāng)C、S想要獲取SM的Binder引用,只需要獲取handle=0的Binder即可。
這么說沒有太直觀的印象,我們一步步剖析。
ServiceManager注冊進(jìn)Binder
SM 注冊進(jìn)Binder驅(qū)動后就會等待來自Binder驅(qū)動的消息,這里列出了兩個最常見的處理消息的Case:
其它進(jìn)程添加服務(wù)到SM里
其它進(jìn)程向SM查詢服務(wù)
SM里維護(hù)著一個鏈表,鏈表的元素是結(jié)構(gòu)體:
主要記錄的是name和handle字段。
當(dāng)SM收到添加服務(wù)的指令后,從Binder驅(qū)動里取出handle和name,并構(gòu)造結(jié)構(gòu)體插入到鏈表。
當(dāng)SM收到查詢服務(wù)的指令后,從Binder驅(qū)動里取出name,并找到鏈表里相同的name,找到后取出handle,最后寫入到Binder驅(qū)動。
4. 進(jìn)程添加服務(wù)到ServiceManager的流程
其它進(jìn)程找到SM
現(xiàn)在SM已經(jīng)翹首以盼其它進(jìn)程的請求了,接著來看看如何添加一個服務(wù)到SM里。
以Java層添加服務(wù)為例,我們選擇振動服務(wù)作為切入點分析。
在system_server 進(jìn)程里構(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里維護(hù)了一個單例,每個進(jìn)程只有一個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對象,而是獲取它在當(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),不同的線程會維護(hù)一個單例。
由此可見,最終還是通過BpBinder發(fā)送消息,進(jìn)而發(fā)送到Binder驅(qū)動。
此時驅(qū)動收到的信息包括不限于:
服務(wù)的名字
ServiceManager的handle
BBinder對象指針
驅(qū)動建立服務(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)起來呢?
重點在:
Parcel.writeStrongBinder(Binder)
也即是說Server端的Java Binder對象在Native層的代表是BBinder。
Binder驅(qū)動記錄了BBinder的地址,當(dāng)有消息過來時通過找到BBinder對象進(jìn)而找到Java層的Binder對象,最終調(diào)用Binder.onTransact()。
5. 進(jìn)程從ServiceManager獲取服務(wù)的流程
其它進(jìn)程找到SM
振動服務(wù)添加完成后,某些進(jìn)程想要獲取振動服務(wù)進(jìn)行振動,比如微信收到消息后需要振動用以提示用戶。
接著來看看如何獲取振動服務(wù)。
private void vibrate() { //獲取振動服務(wù) Vibrator vibrator = (Vibrator)getSystemService(Context.VIBRATOR_SERVICE); //開始振動 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ū)動。
此時驅(qū)動收到的信息包括不限于:
服務(wù)的名字
ServiceManager的handle
Binder驅(qū)動收到消息后,找到SM,并將服務(wù)的名字傳給SM,SM從自己維護(hù)的鏈表里找到服務(wù)名相同的節(jié)點,最終取出該服務(wù)的handle,發(fā)送給Binder驅(qū)動。
用圖表示如下:
對比添加服務(wù)流程和獲取服務(wù)流程,兩者前半部分都很相似,都是先拿到SM的BpBinder引用,然后寫入驅(qū)動,最后由SM進(jìn)程處理。只是對于獲取服務(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ù)的代碼,當(dāng)微信進(jìn)程將查詢命令發(fā)給Binder驅(qū)動后就等待驅(qū)動回復(fù)的結(jié)果,SM查詢到結(jié)果后將handle寫入驅(qū)動,而后微信進(jìn)程從驅(qū)動將結(jié)果讀出并將結(jié)果存入reply字段。
最后通過reply拿到Binder引用,也就是說重點在reply.readStrongBinder()方法。
直接看圖:
如上,通過驅(qū)動返回的handle構(gòu)造BpBinder,最終封裝為Java層的BinderProxy。
至此,獲取服務(wù)流程就結(jié)束了,用圖展示簡化的流程
6. Binder服務(wù)端數(shù)據(jù)接收
微信進(jìn)程拿到振動服務(wù)(在system_server進(jìn)程里)的Binder(BinderProxy)后,就可以調(diào)用振動方法了,而后指令發(fā)送給驅(qū)動,驅(qū)動通過振動服務(wù)的handle找到對應(yīng)的服務(wù)BBinder指針,從而調(diào)用服務(wù)的接收方法。
微信進(jìn)程發(fā)送指令給Binder驅(qū)動前面已經(jīng)分析過,重點來看看system_server進(jìn)程是如何接收并處理指令的。
system_server進(jìn)程啟動的時候就會開啟Binder線程池,并等待驅(qū)動數(shù)據(jù)到來。
當(dāng)system_server進(jìn)程添加振動服務(wù)到SM時,會將Java層的Binder轉(zhuǎn)為Native層的BBinder,并將BBinder對象指針寫入Binder驅(qū)動。
當(dāng)微信進(jìn)程調(diào)用system_server接口時:
微信進(jìn)程調(diào)用BpBinder.transact()將handle和數(shù)據(jù)寫入Binder驅(qū)動
Binder驅(qū)動根據(jù)handle找到system_server進(jìn)程
system_server進(jìn)程從驅(qū)動拿到數(shù)據(jù),并取出BBinder指針,最終調(diào)用到system_server進(jìn)程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端進(jìn)程,進(jìn)而調(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ū)動當(dāng)做一個黑盒,而更重要的是Binder客戶端和服務(wù)端是如何進(jìn)行映射的。
Binder流程比較繞,尤其是IPCThreadStsate作為客戶端的發(fā)送和服務(wù)端的數(shù)據(jù)接收的實體,需要區(qū)分不同的場景。
當(dāng)然,jni基礎(chǔ)知識必不可少。
本文基于Android 10
以上就是Android Binder 通信原理圖文詳解的詳細(xì)內(nèi)容,更多關(guān)于Android Binder 通信原理的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android自定義LocationMarker的實現(xiàn)詳解
這篇文章主要為大家詳細(xì)介紹一個比較簡單的東西:自定義繪制Marker 其實就是自定義view, 跟軌跡沒太多關(guān)聯(lián),感興趣的小伙伴可以跟隨小編一起了解一下2023-02-02Android編程實現(xiàn)讀取手機聯(lián)系人、撥號、發(fā)送短信及長按菜單操作方法實例小結(jié)
這篇文章主要介紹了Android編程實現(xiàn)讀取手機聯(lián)系人、撥號、發(fā)送短信及長按菜單操作方法,以完整實例形式總結(jié)分析了Android編程實現(xiàn)讀取手機聯(lián)系人、撥號、發(fā)送短信及長按菜單等操作的相關(guān)技巧,需要的朋友可以參考下2015-10-10Android?上實現(xiàn)DragonBones換裝功能
這篇文章主要介紹了Android?上實現(xiàn)DragonBones換裝功能,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價值,感興趣的小伙伴可以參考一下2022-06-06Android實現(xiàn)RecyclerView添加分割線的簡便方法
這篇文章主要介紹了Android實現(xiàn)RecyclerView添加分割線的簡便方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-07-07老生常談ProgressBar、ProgessDialog的用法
下面小編就為大家?guī)硪黄仙U凱rogressBar、ProgessDialog的用法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-07-07Android的Activity跳轉(zhuǎn)動畫各種效果整理
Android的Activity跳轉(zhuǎn)就是很生硬的切換界面。其實Android的Activity跳轉(zhuǎn)可以設(shè)置各種動畫,本文整理了一些,還有很多動畫效果,就要靠我們發(fā)揮自己的想象力2013-06-06