前言android
若是你接觸過 跨進程通訊 (IPC),那麼你對Binder必定不陌生
雖然 網上有不少介紹 Binder的文章,但是存在一些問題:淺顯的討論Binder機制 或 一味講解 Binder源碼、邏輯不清楚,最終致使的是讀者們仍是沒法造成一個完整的Binder概念
本文采用 清晰的圖文講解方式,按照 大角度 -> 小角度 去分析Binder,即:
先從 機制、模型的角度 去分析 整個Binder跨進程通訊機制的模型
再 從源碼實現角度,分析 Binder在 Android中的具體實現緩存
從而全方位地介紹 Binder,但願大家會喜歡。安全
請儘可能在PC端而不要在移動端看,不然圖片可能看不清。多線程
目錄架構
1. Binder究竟是什麼?併發
中文即 粘合劑,意思爲粘合了兩個不一樣的進程ide
網上有不少對Binder的定義,但都說不清楚:Binder是跨進程通訊方式、它實現了IBinder接口,是鏈接 ServiceManager的橋樑blabla,估計你們都看暈了,無法很好的理解函數
我認爲:對於Binder的定義,在不一樣場景下其定義不一樣源碼分析
在本文的講解中,按照 大角度 -> 小角度 去分析Binder,即:this
先從 機制、模型的角度 去分析 整個Binder跨進程通訊機制的模型
其中,會詳細分析模型組成中的 Binder驅動
再 從源碼實現角度,分析 Binder在 Android中的具體實現
從而全方位地介紹 Binder,但願大家會喜歡。
2. 知識儲備
在講解Binder前,咱們先了解一些Linux的基礎知識
2.1 進程空間劃分
一個進程空間分爲 用戶空間 & 內核空間(Kernel),即把進程內 用戶 & 內核 隔離開來
兩者區別:
進程間,用戶空間的數據不可共享,因此用戶空間 = 不可共享空間
進程間,內核空間的數據可共享,因此內核空間 = 可共享空間
全部進程共用1個內核空間
進程內 用戶空間 & 內核空間 進行交互 需經過 系統調用,主要經過函數:
copy_from_user():將用戶空間的數據拷貝到內核空間
copy_to_user():將內核空間的數據拷貝到用戶空間
2.2 進程隔離 & 跨進程通訊( IPC )
進程隔離
爲了保證 安全性 & 獨立性,一個進程 不能直接操做或者訪問另外一個進程,即Android的進程是相互獨立、隔離的
跨進程通訊( IPC )
即進程間需進行數據交互、通訊
跨進程通訊的基本原理
a. 而Binder的做用則是:鏈接 兩個進程,實現了mmap()系統調用,主要負責 建立數據接收的緩存空間 & 管理數據接收緩存
b. 注:傳統的跨進程通訊需拷貝數據2次,但Binder機制只需1次,主要是使用到了內存映射,具體下面會詳細說明
2.5 內存映射
具體請看文章:操做系統:圖文詳解 內存映射
3. Binder 跨進程通訊機制 模型
3.1 模型原理圖
Binder 跨進程通訊機制 模型 基於 Client - Server 模式
3.2 模型組成角色說明
此處重點講解 Binder驅動的做用 & 原理:
簡介
跨進程通訊的核心原理
關於其核心原理:內存映射,具體請看文章:操做系統:圖文詳解 內存映射
3.3 模型原理步驟說明
3.4 額外說明
說明1:Client進程、Server進程 & Service Manager 進程之間的交互 都必須經過Binder驅動(使用 open 和 ioctl文件操做函數),而非直接交互
緣由:
1. Client進程、Server進程 & Service Manager進程屬於進程空間的用戶空間,不可進行進程間交互
2. Binder驅動 屬於 進程空間的 內核空間,可進行進程間 & 進程內交互
因此,原理圖可表示爲如下:
虛線表示並不是直接交互
說明2: Binder驅動 & Service Manager進程 屬於 Android基礎架構(即系統已經實現好了);而Client 進程 和 Server 進程 屬於Android應用層(須要開發者本身實現)
因此,在進行跨進程通訊時,開發者只需自定義Client & Server 進程 並 顯式使用上述3個步驟,最終藉助 Android的基本架構功能就可完成進程間通訊
說明3:Binder請求的線程管理
Server進程會建立不少線程來處理Binder請求
Binder模型的線程管理 採用Binder驅動的線程池,並由Binder驅動自身進行管理
而不是由Server進程來管理的
一個進程的Binder線程數默認最大是16,超過的請求會被阻塞等待空閒的Binder線程。
因此,在進程間通訊時處理併發問題時,如使用ContentProvider時,它的CRUD(建立、檢索、更新和刪除)方法只能同時有16個線程同時工做
至此,我相信你們對Binder 跨進程通訊機制 模型 已經有了一個很是清晰的定性認識
下面,我將經過一個實例,分析Binder跨進程通訊機制 模型在 Android中的具體代碼實現方式
即分析 上述步驟在Android中具體是用代碼如何實現的
4. Binder機制 在Android中的具體實現原理
Binder機制在 Android中的實現主要依靠 Binder類,其實現了IBinder 接口
下面會詳細說明
實例說明:Client進程 須要調用 Server進程的加法函數(將整數a和b相加)
即:
Client進程 須要傳兩個整數給 Server進程
Server進程 須要把相加後的結果 返回給Client進程
具體步驟
下面,我會根據Binder 跨進程通訊機制 模型的步驟進行分析
步驟1:註冊服務
過程描述
Server進程 經過Binder驅動 向 Service Manager進程 註冊服務
代碼實現
Server進程 建立 一個 Binder 對象
Binder 實體是 Server進程 在 Binder 驅動中的存在形式
該對象保存 Server 和 ServiceManager 的信息(保存在內核空間中)
Binder 驅動經過 內核空間的Binder 實體 找到用戶空間的Server對象
代碼分析
Binder binder = new Stub();
// 步驟1:建立Binder對象 ->>分析1
// 步驟2:建立 IInterface 接口類 的匿名類
// 建立前,須要預先定義 繼承了IInterface 接口的接口 -->分析3
IInterface plus = new IPlus(){
// 肯定Client進程須要調用的方法
public int add(int a,int b) {
return a+b;
}
// 實現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對象 可根據add two int經過queryLocalIInterface()得到對應IInterface對象(即plus)的引用,可依靠該引用完成對請求方法的調用
// 分析完畢,跳出
<-- 分析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,繼續看步驟2
<-- 分析2:Binder 類 -->
public class Binder implement IBinder{
// Binder機制在Android中的實現主要依靠的是Binder類,其實現了IBinder接口
// IBinder接口:定義了遠程操做對象的基本接口,表明了一種跨進程傳輸的能力
// 系統會爲每一個實現了IBinder接口的對象提供跨進程傳輸能力
// 即Binder類對象具有了跨進程傳輸的能力
void attachInterface(IInterface plus, String descriptor);
// 做用:
// 1. 將(descriptor,plus)做爲(key,value)對存入到Binder對象中的一個Map<String,IInterface>對象中
// 2. 以後,Binder對象 可根據descriptor經過queryLocalIInterface()得到對應IInterface對象(即plus)的引用,可依靠該引用完成對請求方法的調用
IInterface queryLocalInterface(Stringdescriptor) ;
// 做用:根據 參數 descriptor 查找相應的IInterface對象(即plus引用)
boolean onTransact(int code, Parcel data, Parcel reply, int flags);
// 定義:繼承自IBinder接口的
// 做用:執行Client進程所請求的目標方法(子類須要複寫)
// 參數說明:
// code:Client進程請求方法標識符。即Server進程根據該標識肯定所請求的目標方法
// data:目標方法的參數。(Client進程傳進來的,此處就是整數a和b)
// reply:目標方法執行後的結果(返回給Client進程)
// 注:運行在Server進程的Binder線程池中;當Client進程發起遠程請求時,遠程請求會要求系統底層執行回調該方法
final class BinderProxy implements IBinder {
// 即Server進程建立的Binder對象的代理對象類
// 該類屬於Binder的內部類
}
// 回到分析1原處
}
<-- 分析3:IInterface接口實現類 -->
public interface IPlus extends IInterface {
// 繼承自IInterface接口->>分析4
// 定義須要實現的接口方法,即Client進程須要調用的方法
public int add(int a,int b);
// 返回步驟2
}
<-- 分析4:IInterface接口類 -->
// 進程間通訊定義的通用接口
// 經過定義接口,而後再服務端實現接口、客戶端調用接口,就可實現跨進程通訊。
public interface IInterface
{
// 只有一個方法:返回當前接口關聯的 Binder 對象。
public IBinder asBinder();
}
// 回到分析3原處
註冊服務後,Binder驅動持有 Server進程建立的Binder實體
步驟2:獲取服務
Client進程 使用 某個 service前(此處是 相加函數),須 經過Binder驅動 向 ServiceManager進程 獲取相應的Service信息
具體代碼實現過程以下:
此時,Client進程與 Server進程已經創建了鏈接
步驟3:使用服務
Client進程 根據獲取到的 Service信息(Binder代理對象),經過Binder驅動 創建與 該Service所在Server進程通訊的鏈路,並開始使用服務
過程描述
Client進程 將參數(整數a和b)發送到Server進程
Server進程 根據Client進程要求調用 目標方法(即加法函數)
Server進程 將目標方法的結果(即加法後的結果)返回給Client進程
代碼實現過程
步驟1: Client進程 將參數(整數a和b)發送到Server進程
// 1. Client進程 將須要傳送的數據寫入到Parcel對象中
// data = 數據 = 目標方法的參數(Client進程傳進來的,此處就是整數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對象中根據"add two int"經過queryLocalIInterface()查找相應的IInterface對象(即Server建立的plus),Client進程須要調用的相加方法就在該對象中
android.os.Parcel reply = android.os.Parcel.obtain();
// reply:目標方法執行後的結果(此處是相加後的結果)
// 2. 經過 調用代理對象的transact() 將 上述數據發送到Binder驅動
binderproxy.transact(Stub.add, data, reply, 0)
// 參數說明:
// 1. Stub.add:目標方法的標識符(Client進程 和 Server進程 自身約定,可爲任意)
// 2. data :上述的Parcel對象
// 3. reply:返回結果
// 0:可無論
// 注:在發送數據後,Client進程的該線程會暫時被掛起
// 因此,若Server進程執行的耗時操做,請不要使用主線程,以防止ANR
// 3. Binder驅動根據 代理對象 找到對應的真身Binder對象所在的Server 進程(系統自動執行)
// 4. Binder驅動把 數據 發送到Server 進程中,並通知Server 進程執行解包(系統自動執行)
步驟2:Server進程根據Client進要求 調用 目標方法(即加法函數)
// 1. 收到Binder驅動通知後,Server 進程經過回調Binder對象onTransact()進行數據解包 & 調用目標方法
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中的數據
data.enforceInterface("add two int");
// a1. 解析目標方法對象的標識符
int arg0 = data.readInt();
int arg1 = data.readInt();
// a2. 得到目標方法的參數
// b. 根據"add two int"經過queryLocalIInterface()獲取相應的IInterface對象(即Server建立的plus)的引用,經過該對象引用調用方法
int result = this.queryLocalIInterface("add two int") .add( arg0, arg1);
// c. 將計算結果寫入到reply
reply.writeInt(result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
// 2. 將結算結果返回 到Binder驅動
步驟3:Server進程 將目標方法的結果(即加法後的結果)返回給Client進程
// 1. Binder驅動根據 代理對象 沿原路 將結果返回 並通知Client進程獲取返回結果
// 2. 經過代理對象 接收結果(以前被掛起的線程被喚醒)
binderproxy.transact(Stub.ADD, data, reply, 0);
reply.readException();;
result = reply.readInt();
}
}
總結
下面,我用一個原理圖 & 流程圖來總結步驟3的內容
流程圖
5. 優勢
對比 Linux (Android基於Linux)上的其餘進程通訊方式(管道、消息隊列、共享內存、
信號量、Socket),Binder 機制的優勢有:
6. 總結
本文主要詳細講解 跨進程通訊模型 Binder機制 ,總結以下:
特別地,對於從模型結構組成的Binder驅動來講:
整個Binder模型的原理步驟 & 源碼分析
下面我將繼續對 Android中的知識進行講解 ,有興趣能夠繼續關注Carson_Ho的安卓開發筆記