前言
- 如果你接觸過 跨進程通信 (
IPC
)吐葱,那么你對Binder
一定不陌生 - 雖然 網(wǎng)上有很多介紹
Binder
的文章摘刑,可是存在一些問題:淺顯的討論Binder
機制 或 一味講解Binder
源碼志鞍、邏輯不清楚鼓蜒,最終導致的是讀者們還是無法形成一個完整的Binder
概念 - 本文采用 清晰的圖文講解方式,按照 大角度 -> 小角度 去分析
Binder
泻帮,即:- 先從 機制肝陪、模型的角度 去分析 整個
Binder
跨進程通信機制的模型 - 再 從源碼實現(xiàn)角度,分析
Binder
在Android
中的具體實現(xiàn)
- 先從 機制肝陪、模型的角度 去分析 整個
從而全方位地介紹 Binder
刑顺,希望你們會喜歡。
請盡量在PC端而不要在移動端看饲常,否則圖片可能看不清蹲堂。
目錄
1. Binder到底是什么?
中文即 粘合劑贝淤,意思為粘合了兩個不同的進程
網(wǎng)上有很多對
Binder
的定義柒竞,但都說不清楚:Binder
是跨進程通信方式、它實現(xiàn)了IBinder
接口播聪,是連接ServiceManager
的橋梁blabla朽基,估計大家都看暈了,沒法很好的理解我認為:對于
Binder
的定義离陶,在不同場景下其定義不同
在本文的講解中稼虎,按照 大角度 -> 小角度 去分析Binder
,即:
- 先從 機制招刨、模型的角度 去分析 整個
Binder
跨進程通信機制的模型
其中霎俩,會詳細分析模型組成中的
Binder
驅(qū)動
- 再 從源碼實現(xiàn)角度,分析
Binder
在Android
中的具體實現(xiàn)
從而全方位地介紹 Binder
,希望你們會喜歡打却。
2. 知識儲備
在講解Binder
前杉适,我們先了解一些Linux
的基礎(chǔ)知識
2.1 進程空間劃分
- 一個進程空間分為 用戶空間 & 內(nèi)核空間(
Kernel
),即把進程內(nèi) 用戶 & 內(nèi)核 隔離開來 - 二者區(qū)別:
- 進程間柳击,用戶空間的數(shù)據(jù)不可共享猿推,所以用戶空間 = 不可共享空間
- 進程間,內(nèi)核空間的數(shù)據(jù)可共享捌肴,所以內(nèi)核空間 = 可共享空間
所有進程共用1個內(nèi)核空間
- 進程內(nèi) 用戶空間 & 內(nèi)核空間 進行交互 需通過 系統(tǒng)調(diào)用蹬叭,主要通過函數(shù):
- copy_from_user():將用戶空間的數(shù)據(jù)拷貝到內(nèi)核空間
- copy_to_user():將內(nèi)核空間的數(shù)據(jù)拷貝到用戶空間
2.2 進程隔離 & 跨進程通信( IPC )
進程隔離
為了保證 安全性 & 獨立性,一個進程 不能直接操作或者訪問另一個進程哭靖,即Android
的進程是相互獨立具垫、隔離的跨進程通信(
IPC
)
即進程間需進行數(shù)據(jù)交互、通信跨進程通信的基本原理
a. 而
Binder
的作用則是:連接 兩個進程试幽,實現(xiàn)了mmap()系統(tǒng)調(diào)用筝蚕,主要負責 創(chuàng)建數(shù)據(jù)接收的緩存空間 & 管理數(shù)據(jù)接收緩存
b. 注:傳統(tǒng)的跨進程通信需拷貝數(shù)據(jù)2次,但Binder
機制只需1次铺坞,主要是使用到了內(nèi)存映射起宽,具體下面會詳細說明
2.5 內(nèi)存映射
具體請看文章:操作系統(tǒng):圖文詳解 內(nèi)存映射
3. Binder 跨進程通信機制 模型
3.1 模型原理圖
Binder
跨進程通信機制 模型 基于 Client - Server
模式
3.2 模型組成角色說明
此處重點講解 Binder
驅(qū)動作用中的跨進程通信的原理:
- 簡介
- 跨進程通信的核心原理
關(guān)于其核心原理:內(nèi)存映射,具體請看文章:操作系統(tǒng):圖文詳解 內(nèi)存映射
3.3 模型原理步驟說明
3.4 額外說明
說明1:Client
進程济榨、Server
進程 & Service Manager
進程之間的交互 都必須通過Binder
驅(qū)動(使用 open
和 ioctl
文件操作函數(shù))坯沪,而非直接交互
原因:
-
Client
進程、Server
進程 &Service Manager
進程屬于進程空間的用戶空間擒滑,不可進行進程間交互 -
Binder
驅(qū)動 屬于 進程空間的 內(nèi)核空間腐晾,可進行進程間 & 進程內(nèi)交互
所以,原理圖可表示為以下:
虛線表示并非直接交互
說明2: Binder
驅(qū)動 & Service Manager
進程 屬于 Android
基礎(chǔ)架構(gòu)(即系統(tǒng)已經(jīng)實現(xiàn)好了)丐一;而Client
進程 和 Server
進程 屬于Android
應用層(需要開發(fā)者自己實現(xiàn))
所以藻糖,在進行跨進程通信時,開發(fā)者只需自定義Client
& Server
進程 并 顯式使用上述3個步驟库车,最終借助 Android
的基本架構(gòu)功能就可完成進程間通信
說明3:Binder請求的線程管理
-
Server
進程會創(chuàng)建很多線程來處理Binder
請求 -
Binder
模型的線程管理 采用Binder
驅(qū)動的線程池巨柒,并由Binder
驅(qū)動自身進行管理
而不是由
Server
進程來管理的
- 一個進程的
Binder
線程數(shù)默認最大是16,超過的請求會被阻塞等待空閑的Binder線程柠衍。
所以洋满,在進程間通信時處理并發(fā)問題時,如使用
ContentProvider
時珍坊,它的CRUD
(創(chuàng)建牺勾、檢索、更新和刪除)方法只能同時有16個線程同時工作
- 至此阵漏,我相信大家對
Binder
跨進程通信機制 模型 已經(jīng)有了一個非常清晰的定性認識 - 下面禽最,我將通過一個實例腺怯,分析
Binder
跨進程通信機制 模型在Android
中的具體代碼實現(xiàn)方式
即分析 上述步驟在
Android
中具體是用代碼如何實現(xiàn)的
4. Binder機制 在Android中的具體實現(xiàn)原理
-
Binder
機制在Android
中的實現(xiàn)主要依靠Binder
類,其實現(xiàn)了IBinder
接口
下面會詳細說明
- 實例說明:
Client
進程 需要調(diào)用Server
進程的加法函數(shù)(將整數(shù)a和b相加)
即:
Client
進程 需要傳兩個整數(shù)給Server
進程Server
進程 需要把相加后的結(jié)果 返回給Client
進程
- 具體步驟
下面川无,我會根據(jù)Binder
跨進程通信機制 模型的步驟進行分析
步驟1:注冊服務
- 過程描述
Server
進程 通過Binder
驅(qū)動 向Service Manager
進程 注冊服務 - 代碼實現(xiàn)
Server
進程 創(chuàng)建 一個Binder
對象
Binder
實體是Server
進程 在Binder
驅(qū)動中的存在形式- 該對象保存
Server
和ServiceManager
的信息(保存在內(nèi)核空間中)Binder
驅(qū)動通過 內(nèi)核空間的Binder
實體 找到用戶空間的Server
對象
- 代碼分析
Binder binder = new Stub();
// 步驟1:創(chuàng)建Binder對象 ->>分析1
// 步驟2:創(chuàng)建 IInterface 接口類 的匿名類
// 創(chuàng)建前呛占,需要預先定義 繼承了IInterface 接口的接口 -->分析3
IInterface plus = new IPlus(){
// 確定Client進程需要調(diào)用的方法
public int add(int a,int b) {
return a+b;
}
// 實現(xiàn)IInterface接口中唯一的方法
public IBinder asBinder(){
return null ;
}
};
// 步驟3
binder.attachInterface(plus,"add two int");
// 1. 將(add two int懦趋,plus)作為(key,value)對存入到Binder對象中的一個Map<String,IInterface>對象中
// 2. 之后晾虑,Binder對象 可根據(jù)add two int通過queryLocalIInterface()獲得對應IInterface對象(即plus)的引用,可依靠該引用完成對請求方法的調(diào)用
// 分析完畢仅叫,跳出
<-- 分析1:Stub類 -->
public class Stub extends Binder {
// 繼承自Binder類 ->>分析2
// 復寫onTransact()
@Override
boolean onTransact(int code, Parcel data, Parcel reply, int flags){
// 具體邏輯等到步驟3再具體講解帜篇,此處先跳過
switch (code) {
case Stub.add: {
data.enforceInterface("add two int");
int arg0 = data.readInt();
int arg1 = data.readInt();
int result = this.queryLocalIInterface("add two int") .add( arg0, arg1);
reply.writeInt(result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
// 回到上面的步驟1,繼續(xù)看步驟2
<-- 分析2:Binder 類 -->
public class Binder implement IBinder{
// Binder機制在Android中的實現(xiàn)主要依靠的是Binder類诫咱,其實現(xiàn)了IBinder接口
// IBinder接口:定義了遠程操作對象的基本接口笙隙,代表了一種跨進程傳輸?shù)哪芰? // 系統(tǒng)會為每個實現(xiàn)了IBinder接口的對象提供跨進程傳輸能力
// 即Binder類對象具備了跨進程傳輸?shù)哪芰?
void attachInterface(IInterface plus, String descriptor);
// 作用:
// 1. 將(descriptor坎缭,plus)作為(key,value)對存入到Binder對象中的一個Map<String,IInterface>對象中
// 2. 之后竟痰,Binder對象 可根據(jù)descriptor通過queryLocalIInterface()獲得對應IInterface對象(即plus)的引用,可依靠該引用完成對請求方法的調(diào)用
IInterface queryLocalInterface(Stringdescriptor) 掏呼;
// 作用:根據(jù) 參數(shù) descriptor 查找相應的IInterface對象(即plus引用)
boolean onTransact(int code, Parcel data, Parcel reply, int flags)坏快;
// 定義:繼承自IBinder接口的
// 作用:執(zhí)行Client進程所請求的目標方法(子類需要復寫)
// 參數(shù)說明:
// code:Client進程請求方法標識符。即Server進程根據(jù)該標識確定所請求的目標方法
// data:目標方法的參數(shù)憎夷。(Client進程傳進來的莽鸿,此處就是整數(shù)a和b)
// reply:目標方法執(zhí)行后的結(jié)果(返回給Client進程)
// 注:運行在Server進程的Binder線程池中;當Client進程發(fā)起遠程請求時拾给,遠程請求會要求系統(tǒng)底層執(zhí)行回調(diào)該方法
final class BinderProxy implements IBinder {
// 即Server進程創(chuàng)建的Binder對象的代理對象類
// 該類屬于Binder的內(nèi)部類
}
// 回到分析1原處
}
<-- 分析3:IInterface接口實現(xiàn)類 -->
public interface IPlus extends IInterface {
// 繼承自IInterface接口->>分析4
// 定義需要實現(xiàn)的接口方法祥得,即Client進程需要調(diào)用的方法
public int add(int a,int b);
// 返回步驟2
}
<-- 分析4:IInterface接口類 -->
// 進程間通信定義的通用接口
// 通過定義接口,然后再服務端實現(xiàn)接口蒋得、客戶端調(diào)用接口级及,就可實現(xiàn)跨進程通信。
public interface IInterface
{
// 只有一個方法:返回當前接口關(guān)聯(lián)的 Binder 對象窄锅。
public IBinder asBinder();
}
// 回到分析3原處
注冊服務后,Binder
驅(qū)動持有 Server
進程創(chuàng)建的Binder
實體
步驟2:獲取服務
-
Client
進程 使用 某個service
前(此處是 相加函數(shù))缰雇,須 通過Binder
驅(qū)動 向ServiceManager
進程 獲取相應的Service
信息 - 具體代碼實現(xiàn)過程如下:
此時入偷,Client
進程與 Server
進程已經(jīng)建立了連接
步驟3:使用服務
Client
進程 根據(jù)獲取到的 Service
信息(Binder
代理對象),通過Binder
驅(qū)動 建立與 該Service
所在Server
進程通信的鏈路械哟,并開始使用服務
-
過程描述
-
Client
進程 將參數(shù)(整數(shù)a和b)發(fā)送到Server
進程 -
Server
進程 根據(jù)Client
進程要求調(diào)用 目標方法(即加法函數(shù)) -
Server
進程 將目標方法的結(jié)果(即加法后的結(jié)果)返回給Client
進程
-
代碼實現(xiàn)過程
步驟1: Client
進程 將參數(shù)(整數(shù)a和b)發(fā)送到Server
進程
// 1. Client進程 將需要傳送的數(shù)據(jù)寫入到Parcel對象中
// data = 數(shù)據(jù) = 目標方法的參數(shù)(Client進程傳進來的疏之,此處就是整數(shù)a和b) + IInterface接口對象的標識符descriptor
android.os.Parcel data = android.os.Parcel.obtain();
data.writeInt(a);
data.writeInt(b);
data.writeInterfaceToken("add two int");;
// 方法對象標識符讓Server進程在Binder對象中根據(jù)"add two int"通過queryLocalIInterface()查找相應的IInterface對象(即Server創(chuàng)建的plus)暇咆,Client進程需要調(diào)用的相加方法就在該對象中
android.os.Parcel reply = android.os.Parcel.obtain();
// reply:目標方法執(zhí)行后的結(jié)果(此處是相加后的結(jié)果)
// 2. 通過 調(diào)用代理對象的transact() 將 上述數(shù)據(jù)發(fā)送到Binder驅(qū)動
binderproxy.transact(Stub.add, data, reply, 0)
// 參數(shù)說明:
// 1. Stub.add:目標方法的標識符(Client進程 和 Server進程 自身約定锋爪,可為任意)
// 2. data :上述的Parcel對象
// 3. reply:返回結(jié)果
// 0:可不管
// 注:在發(fā)送數(shù)據(jù)后丙曙,Client進程的該線程會暫時被掛起
// 所以,若Server進程執(zhí)行的耗時操作其骄,請不要使用主線程亏镰,以防止ANR
// 3. Binder驅(qū)動根據(jù) 代理對象 找到對應的真身Binder對象所在的Server 進程(系統(tǒng)自動執(zhí)行)
// 4. Binder驅(qū)動把 數(shù)據(jù) 發(fā)送到Server 進程中,并通知Server 進程執(zhí)行解包(系統(tǒng)自動執(zhí)行)
步驟2:Server
進程根據(jù)Client
進要求 調(diào)用 目標方法(即加法函數(shù))
// 1. 收到Binder驅(qū)動通知后拯爽,Server 進程通過回調(diào)Binder對象onTransact()進行數(shù)據(jù)解包 & 調(diào)用目標方法
public class Stub extends Binder {
// 復寫onTransact()
@Override
boolean onTransact(int code, Parcel data, Parcel reply, int flags){
// code即在transact()中約定的目標方法的標識符
switch (code) {
case Stub.add: {
// a. 解包Parcel中的數(shù)據(jù)
data.enforceInterface("add two int");
// a1. 解析目標方法對象的標識符
int arg0 = data.readInt();
int arg1 = data.readInt();
// a2. 獲得目標方法的參數(shù)
// b. 根據(jù)"add two int"通過queryLocalIInterface()獲取相應的IInterface對象(即Server創(chuàng)建的plus)的引用索抓,通過該對象引用調(diào)用方法
int result = this.queryLocalIInterface("add two int") .add( arg0, arg1);
// c. 將計算結(jié)果寫入到reply
reply.writeInt(result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
// 2. 將結(jié)算結(jié)果返回 到Binder驅(qū)動
步驟3:Server
進程 將目標方法的結(jié)果(即加法后的結(jié)果)返回給Client
進程
// 1. Binder驅(qū)動根據(jù) 代理對象 沿原路 將結(jié)果返回 并通知Client進程獲取返回結(jié)果
// 2. 通過代理對象 接收結(jié)果(之前被掛起的線程被喚醒)
binderproxy.transact(Stub.ADD, data, reply, 0);
reply.readException();毯炮;
result = reply.readInt()逼肯;
}
}
- 總結(jié)
下面,我用一個原理圖 & 流程圖來總結(jié)步驟3的內(nèi)容
5. 優(yōu)點
對比 Linux
(Android
基于Linux
)上的其他進程通信方式(管道桃煎、消息隊列篮幢、共享內(nèi)存、
信號量为迈、Socket
)三椿,Binder
機制的優(yōu)點有:
6. 總結(jié)
- 本文主要詳細講解 跨進程通信模型
Binder
機制 ,總結(jié)如下:
特別地曲尸,對于從模型結(jié)構(gòu)組成的Binder驅(qū)動來說:
- 整個
Binder
模型的原理步驟 & 源碼分析
- 看完本文的
Binder
機制原理赋续,繼續(xù)閱讀AIDL
的內(nèi)容會更加好,具體請看我的文章Android:遠程服務Service(含AIDL & IPC講解) - Carson帶你學四大組件文章系列:
Carson帶你學Android:頁面活動-Activity
Carson帶你學Android:廣播-BroadcastReceiver
Carson帶你學Android:服務-Service
Carson帶你學Android:內(nèi)存承載器-ContentProvider
歡迎關(guān)注Carson_Ho的簡書
不定期分享關(guān)于安卓開發(fā)的干貨另患,追求短纽乱、平、快昆箕,但卻不缺深度鸦列。