Android Binder機(jī)制

1. Binder到底是什么德迹?

<1> 中文即 粘合劑极阅,意思為粘合了兩個不同的進(jìn)程
<2> 網(wǎng)上有很多對Binder的定義,但都說不清楚:Binder是跨進(jìn)程通信方式陆爽、它實(shí)現(xiàn)了
IBinder接口言秸,是連接 ServiceManager的橋梁blabla软能,估計(jì)大家都看暈了,沒法很好的理解

<3> 我認(rèn)為:對于Binder的定義举畸,在不同場景下其定義不同

在本文的講解中查排,按照 大角度 -> 小角度 去分析Binder,即:

<1> 先從 機(jī)制抄沮、模型的角度 去分析 整個Binder跨進(jìn)程通信機(jī)制的模型

其中跋核,會詳細(xì)分析模型組成中的 Binder驅(qū)動

<2> 再從源碼實(shí)現(xiàn)角度,分析Binder在 Android中的具體實(shí)現(xiàn)

從而全方位地介紹 Binder叛买,希望你們會喜歡砂代。

2. 知識儲備

在講解Binder前,我們先了解一些基礎(chǔ)知識

2.1 進(jìn)程空間分配

<1> 一個進(jìn)程空間分為 用戶空間 & 內(nèi)核空間(Kernel)率挣,即把進(jìn)程內(nèi) 用戶 & 內(nèi)核 隔離開來
<2> 二者區(qū)別:
進(jìn)程間刻伊,用戶空間的數(shù)據(jù)不可共享,所以用戶空間 = 不可共享空間
進(jìn)程間椒功,內(nèi)核空間的數(shù)據(jù)可共享捶箱,所以內(nèi)核空間 = 可共享空間
<3> 進(jìn)程內(nèi) 用戶 與 內(nèi)核 進(jìn)行交互 稱為系統(tǒng)調(diào)用

2.2 進(jìn)程隔離

為了保證 安全性 & 獨(dú)立性,一個進(jìn)程 不能直接操作或者訪問另一個進(jìn)程动漾,即Android的進(jìn)程是相互獨(dú)立丁屎、隔離的

2.3 跨進(jìn)程通信( IPC )

<1> 隔離后,由于某些需求旱眯,進(jìn)程間 需要合作 / 交互
<2>跨進(jìn)程間通信的原理
1.先通過 進(jìn)程間 的內(nèi)核空間進(jìn)行 數(shù)據(jù)交互
2.再通過 進(jìn)程內(nèi) 的用戶空間 & 內(nèi)核空間進(jìn)行 數(shù)據(jù)交互悦屏,從而實(shí)現(xiàn) 進(jìn)程間的用戶空間 的數(shù)據(jù)交互

而Binder节沦,就是充當(dāng) 連接 兩個進(jìn)程(內(nèi)核空間)的通道键思。

3. Binder 跨進(jìn)程通信機(jī)制 模型

3.1 模型原理

Binder 跨進(jìn)程通信機(jī)制 模型 基于 Client - Server 模式础爬,模型原理圖如下:

binder001.png

3.2 額外說明

說明1: Client進(jìn)程、Server進(jìn)程 & Service Manager 進(jìn)程之間的交互都必須通過Binder驅(qū)動(使用 open 和 ioctl文件操作函數(shù))吼鳞,而非直接交互 **

原因:

  1. Client進(jìn)程看蚜、Server進(jìn)程 & Service Manager進(jìn)程屬于進(jìn)程空間的用戶空間,不可進(jìn)行進(jìn)程間交互
  2. Binder驅(qū)動 屬于 進(jìn)程空間的 內(nèi)核空間赔桌,可進(jìn)行進(jìn)程間 & 進(jìn)程內(nèi)交互

所以供炎,原理圖可表示為以下:

說明2: Binder驅(qū)動 & Service Manager進(jìn)程 屬于 Android基礎(chǔ)架構(gòu)(即系統(tǒng)已經(jīng)實(shí)現(xiàn)好了);而Client 進(jìn)程 和 Server 進(jìn)程 屬于Android應(yīng)用層(需要開發(fā)者自己實(shí)現(xiàn))

所以疾党,在進(jìn)行跨進(jìn)程通信時音诫,開發(fā)者只需自定義Client & Server 進(jìn)程 并 顯式使用上述3個步驟,最終借助 Android的基本架構(gòu)功能就可完成進(jìn)程間通信

說明3: Binder請求的線程管理
<li> Server進(jìn)程會創(chuàng)建很多線程來處理Binder請求
<li> 管理Binder模型的線程是采用Binder驅(qū)動的線程池雪位,并由Binder驅(qū)動自身進(jìn)行管理竭钝,而不是由Server進(jìn)程來管理的
<li> 一個進(jìn)程的Binder線程數(shù)默認(rèn)最大是16,超過的請求會被阻塞等待空閑的Binder線程雹洗。

所以香罐,在進(jìn)程間通信時處理并發(fā)問題時,如使用ContentProvider時时肿,它的CRUD(創(chuàng)建庇茫、檢索、更新和刪除)方法只能同時有16個線程同時工作

<li> 至此螃成,我相信大家對Binder 跨進(jìn)程通信機(jī)制 模型 已經(jīng)有了一個非常清晰的定性認(rèn)識

<li> 下面旦签,我將通過一個實(shí)例,分析Binder跨進(jìn)程通信機(jī)制 模型在 Android中的具體代碼實(shí)現(xiàn)方式

4. Binder機(jī)制 在Android中的具體實(shí)現(xiàn)原理

<li> Binder機(jī)制在 Android中的實(shí)現(xiàn)主要依靠 Binder類寸宏,其實(shí)現(xiàn)了IBinder 接口
<li> 實(shí)例說明:Client進(jìn)程 需要調(diào)用 Server進(jìn)程的加法函數(shù)(將整數(shù)a和b相加)

1.Client進(jìn)程 需要傳兩個整數(shù)給 Server進(jìn)程
2.Server進(jìn)程 需要把相加后的結(jié)果 返回給Client進(jìn)程
<li> 具體步驟
下面宁炫,我會根據(jù)Binder 跨進(jìn)程通信機(jī)制 模型的步驟進(jìn)行分析

4.1 步驟1:注冊服務(wù)

<li> 過程描述
Server進(jìn)程 通過Binder驅(qū)動 向 Service Manager進(jìn)程 注冊服務(wù)
<li> 代碼實(shí)現(xiàn)
Server進(jìn)程 創(chuàng)建 一個 Binder 對象

1.Binder 實(shí)體是 Server進(jìn)程 在 Binder 驅(qū)動中的存在形式
2.該對象保存 Server 和 ServiceManager 的信息(保存在內(nèi)核空間中)
3.Binder 驅(qū)動通過 內(nèi)核空間的Binder 實(shí)體 找到用戶空間的Server對象
<li> 代碼分析

Binder binder = new Stub();
    // 步驟1:創(chuàng)建Binder對象 ->>分析1

    // 步驟2:創(chuàng)建 IInterface 接口類 的匿名類
    // 創(chuàng)建前,需要預(yù)先定義 繼承了IInterface 接口的接口 -->分析3
    IInterface plus = new IPlus(){

          // 確定Client進(jìn)程需要調(diào)用的方法
          public int add(int a,int b) {
               return a+b;
         }

          // 實(shí)現(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()獲得對應(yīng)IInterface對象(即plus)的引用覆醇,可依靠該引用完成對請求方法的調(diào)用
        // 分析完畢朵纷,跳出


<-- 分析1:Stub類 -->
    public class Stub extends Binder {
    // 繼承自Binder類 ->>分析2

          // 復(fù)寫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機(jī)制在Android中的實(shí)現(xiàn)主要依靠的是Binder類袍辞,其實(shí)現(xiàn)了IBinder接口
    // IBinder接口:定義了遠(yuǎn)程操作對象的基本接口,代表了一種跨進(jìn)程傳輸?shù)哪芰?    // 系統(tǒng)會為每個實(shí)現(xiàn)了IBinder接口的對象提供跨進(jìn)程傳輸能力
    // 即Binder類對象具備了跨進(jìn)程傳輸?shù)哪芰?
        void attachInterface(IInterface plus, String descriptor)常摧;
        // 作用:
          // 1. 將(descriptor搅吁,plus)作為(key,value)對存入到Binder對象中的一個Map<String,IInterface>對象中
          // 2. 之后威创,Binder對象 可根據(jù)descriptor通過queryLocalIInterface()獲得對應(yīng)IInterface對象(即plus)的引用,可依靠該引用完成對請求方法的調(diào)用

        IInterface queryLocalInterface(Stringdescriptor) 谎懦;
        // 作用:根據(jù) 參數(shù) descriptor 查找相應(yīng)的IInterface對象(即plus引用)

        boolean onTransact(int code, Parcel data, Parcel reply, int flags)肚豺;
        // 定義:繼承自IBinder接口的
        // 作用:執(zhí)行Client進(jìn)程所請求的目標(biāo)方法(子類需要復(fù)寫)
        // 參數(shù)說明:
        // code:Client進(jìn)程請求方法標(biāo)識符。即Server進(jìn)程根據(jù)該標(biāo)識確定所請求的目標(biāo)方法
        // data:目標(biāo)方法的參數(shù)界拦。(Client進(jìn)程傳進(jìn)來的吸申,此處就是整數(shù)a和b)
        // reply:目標(biāo)方法執(zhí)行后的結(jié)果(返回給Client進(jìn)程)
         // 注:運(yùn)行在Server進(jìn)程的Binder線程池中;當(dāng)Client進(jìn)程發(fā)起遠(yuǎn)程請求時享甸,遠(yuǎn)程請求會要求系統(tǒng)底層執(zhí)行回調(diào)該方法

        final class BinderProxy implements IBinder {
         // 即Server進(jìn)程創(chuàng)建的Binder對象的代理對象類
         // 該類屬于Binder的內(nèi)部類
        }
        // 回到分析1原處
}

<-- 分析3:IInterface接口實(shí)現(xiàn)類 -->

 public interface IPlus extends IInterface {
          // 繼承自IInterface接口->>分析4
          // 定義需要實(shí)現(xiàn)的接口方法截碴,即Client進(jìn)程需要調(diào)用的方法
         public int add(int a,int b);
// 返回步驟2
}

<-- 分析4:IInterface接口類 -->
// 進(jìn)程間通信定義的通用接口
// 通過定義接口,然后再服務(wù)端實(shí)現(xiàn)接口蛉威、客戶端調(diào)用接口日丹,就可實(shí)現(xiàn)跨進(jìn)程通信。
public interface IInterface
{
    // 只有一個方法:返回當(dāng)前接口關(guān)聯(lián)的 Binder 對象蚯嫌。
    public IBinder asBinder();
}
  // 回到分析3原處
  

注冊服務(wù)后哲虾,Binder驅(qū)動持有 Server進(jìn)程創(chuàng)建的Binder實(shí)體

步驟2:獲取服務(wù)

<li> Client進(jìn)程 使用 某個 service前(此處是 相加函數(shù)),須 通過Binder驅(qū)動 向 ServiceManager進(jìn)程 獲取相應(yīng)的Service信息
<li> 具體代碼實(shí)現(xiàn)過程如下:

此時齐帚,Client進(jìn)程與 Server進(jìn)程已經(jīng)建立了連接

步驟3:使用服務(wù)

Client進(jìn)程 根據(jù)獲取到的 Service信息(Binder代理對象)妒牙,通過Binder驅(qū)動 建立與 該Service所在Server進(jìn)程通信的鏈路,并開始使用服務(wù)

<li> 過程描述

  1. Client進(jìn)程 將參數(shù)(整數(shù)a和b)發(fā)送到Server進(jìn)程
  2. Server進(jìn)程 根據(jù)Client進(jìn)程要求調(diào)用 目標(biāo)方法(即加法函數(shù))
  3. Server進(jìn)程 將目標(biāo)方法的結(jié)果(即加法后的結(jié)果)返回給Client進(jìn)程
    <li> 代碼實(shí)現(xiàn)過程
    步驟1: Client進(jìn)程 將參數(shù)(整數(shù)a和b)發(fā)送到Server進(jìn)程
// 1. Client進(jìn)程 將需要傳送的數(shù)據(jù)寫入到Parcel對象中
// data = 數(shù)據(jù) = 目標(biāo)方法的參數(shù)(Client進(jìn)程傳進(jìn)來的对妄,此處就是整數(shù)a和b) + IInterface接口對象的標(biāo)識符descriptor
  android.os.Parcel data = android.os.Parcel.obtain();
  data.writeInt(a); 
  data.writeInt(b); 

  data.writeInterfaceToken("add two int");湘今;
  // 方法對象標(biāo)識符讓Server進(jìn)程在Binder對象中根據(jù)"add two int"通過queryLocalIInterface()查找相應(yīng)的IInterface對象(即Server創(chuàng)建的plus),Client進(jìn)程需要調(diào)用的相加方法就在該對象中

  android.os.Parcel reply = android.os.Parcel.obtain();
  // reply:目標(biāo)方法執(zhí)行后的結(jié)果(此處是相加后的結(jié)果)

// 2. 通過 調(diào)用代理對象的transact() 將 上述數(shù)據(jù)發(fā)送到Binder驅(qū)動
  binderproxy.transact(Stub.add, data, reply, 0)
  // 參數(shù)說明:
    // 1. Stub.add:目標(biāo)方法的標(biāo)識符(Client進(jìn)程 和 Server進(jìn)程 自身約定剪菱,可為任意)
    // 2. data :上述的Parcel對象
    // 3. reply:返回結(jié)果
    // 0:可不管

// 注:在發(fā)送數(shù)據(jù)后摩瞎,Client進(jìn)程的該線程會暫時被掛起
// 所以,若Server進(jìn)程執(zhí)行的耗時操作孝常,請不要使用主線程旗们,以防止ANR


// 3. Binder驅(qū)動根據(jù) 代理對象 找到對應(yīng)的真身Binder對象所在的Server 進(jìn)程(系統(tǒng)自動執(zhí)行)
// 4. Binder驅(qū)動把 數(shù)據(jù) 發(fā)送到Server 進(jìn)程中,并通知Server 進(jìn)程執(zhí)行解包(系統(tǒng)自動執(zhí)行)

步驟2:Server進(jìn)程根據(jù)Client進(jìn)要求 調(diào)用 目標(biāo)方法(即加法函數(shù))

// 1. 收到Binder驅(qū)動通知后构灸,Server 進(jìn)程通過回調(diào)Binder對象onTransact()進(jìn)行數(shù)據(jù)解包 & 調(diào)用目標(biāo)方法
  public class Stub extends Binder {

          // 復(fù)寫onTransact()
          @Override
          boolean onTransact(int code, Parcel data, Parcel reply, int flags){
          // code即在transact()中約定的目標(biāo)方法的標(biāo)識符

          switch (code) { 
                case Stub.add: { 
                  // a. 解包Parcel中的數(shù)據(jù)
                       data.enforceInterface("add two int"); 
                        // a1. 解析目標(biāo)方法對象的標(biāo)識符

                       int  arg0  = data.readInt();
                       int  arg1  = data.readInt();
                       // a2. 獲得目標(biāo)方法的參數(shù)

                       // b. 根據(jù)"add two int"通過queryLocalIInterface()獲取相應(yīng)的IInterface對象(即Server創(chuàng)建的plus)的引用上渴,通過該對象引用調(diào)用方法
                       int  result = this.queryLocalIInterface("add two int") .add( arg0,  arg1); 

                        // c. 將計(jì)算結(jié)果寫入到reply
                        reply.writeInt(result); 

                        return true; 
                  }
           } 
      return super.onTransact(code, data, reply, flags); 
      // 2. 將結(jié)算結(jié)果返回 到Binder驅(qū)動

**步驟3:Server進(jìn)程 將目標(biāo)方法的結(jié)果(即加法后的結(jié)果)返回給Client進(jìn)程 **

// 1. Binder驅(qū)動根據(jù) 代理對象 沿原路 將結(jié)果返回 并通知Client進(jìn)程獲取返回結(jié)果
  // 2. 通過代理對象 接收結(jié)果(之前被掛起的線程被喚醒)

    binderproxy.transact(Stub.ADD, data, reply, 0);
    reply.readException();喜颁;
    result = reply.readInt()稠氮;
          }
}

<li> 總結(jié)
下面,我用一個原理圖 & 流程圖來總結(jié)步驟3的內(nèi)容

5. 優(yōu)點(diǎn)

對比 Linux (Android基于Linux)上的其他進(jìn)程通信方式(管道/消息隊(duì)列/共享內(nèi)存/信號量/Socket)半开,Binder 機(jī)制的優(yōu)點(diǎn)有:

<li> 高效

1.Binder數(shù)據(jù)拷貝只需要一次隔披,而管道、消息隊(duì)列寂拆、Socket都需要2次
2.通過驅(qū)動在內(nèi)核空間拷貝數(shù)據(jù)奢米,不需要額外的同步處理
<li> 安全性高
Binder 機(jī)制為每個進(jìn)程分配了 UID/PID 來作為鑒別身份的標(biāo)示抓韩,并且在 Binder 通信時會根據(jù) UID/PID 進(jìn)行有效性檢測

  1. 傳統(tǒng)的進(jìn)程通信方式對于通信雙方的身份并沒有做出嚴(yán)格的驗(yàn)證
  2. 如,Socket通信 ip地址是客戶端手動填入鬓长,容易出現(xiàn)偽造

<li>使用簡單
1.采用Client/Server 架構(gòu)
2.實(shí)現(xiàn) 面向?qū)ο?的調(diào)用方式谒拴,即在使用Binder時就和調(diào)用一個本地對象實(shí)例一樣

6. 總結(jié)

<li> 本文主要詳細(xì)講解 跨進(jìn)程通信模型 Binder機(jī)制 ,總結(jié)如下:

binder.png

贊賞

微信

??????
支付寶

http://blog.csdn.net/carson_ho
https://zhuanlan.zhihu.com/p/35519585

https://lrh1993.gitbooks.io/android_interview_guide/content/android/advance/binder.html

http://blog.csdn.net/carson_ho/article/details/73560642
https://github.com/LRH1993/android_interview

http://www.reibang.com/p/adaa1a39a274

https://www.zhihu.com/question/39440766/answer/89210950

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末痢士,一起剝皮案震驚了整個濱河市彪薛,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌怠蹂,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,723評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件少态,死亡現(xiàn)場離奇詭異城侧,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)彼妻,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評論 2 382
  • 文/潘曉璐 我一進(jìn)店門嫌佑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人侨歉,你說我怎么就攤上這事屋摇。” “怎么了幽邓?”我有些...
    開封第一講書人閱讀 152,998評論 0 344
  • 文/不壞的土叔 我叫張陵炮温,是天一觀的道長。 經(jīng)常有香客問我牵舵,道長柒啤,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,323評論 1 279
  • 正文 為了忘掉前任畸颅,我火速辦了婚禮担巩,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘没炒。我一直安慰自己涛癌,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,355評論 5 374
  • 文/花漫 我一把揭開白布送火。 她就那樣靜靜地躺著拳话,像睡著了一般。 火紅的嫁衣襯著肌膚如雪漾脂。 梳的紋絲不亂的頭發(fā)上假颇,一...
    開封第一講書人閱讀 49,079評論 1 285
  • 那天,我揣著相機(jī)與錄音骨稿,去河邊找鬼笨鸡。 笑死姜钳,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的形耗。 我是一名探鬼主播哥桥,決...
    沈念sama閱讀 38,389評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼激涤!你這毒婦竟也來了拟糕?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,019評論 0 259
  • 序言:老撾萬榮一對情侶失蹤倦踢,失蹤者是張志新(化名)和其女友劉穎送滞,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體辱挥,經(jīng)...
    沈念sama閱讀 43,519評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡犁嗅,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,971評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了晤碘。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片褂微。...
    茶點(diǎn)故事閱讀 38,100評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖园爷,靈堂內(nèi)的尸體忽然破棺而出宠蚂,到底是詐尸還是另有隱情,我是刑警寧澤童社,帶...
    沈念sama閱讀 33,738評論 4 324
  • 正文 年R本政府宣布求厕,位于F島的核電站,受9級特大地震影響叠洗,放射性物質(zhì)發(fā)生泄漏甘改。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,293評論 3 307
  • 文/蒙蒙 一灭抑、第九天 我趴在偏房一處隱蔽的房頂上張望十艾。 院中可真熱鬧,春花似錦腾节、人聲如沸忘嫉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽庆冕。三九已至,卻和暖如春劈榨,著一層夾襖步出監(jiān)牢的瞬間访递,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評論 1 262
  • 我被黑心中介騙來泰國打工同辣, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留拷姿,地道東北人惭载。 一個月前我還...
    沈念sama閱讀 45,547評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像响巢,于是被迫代替她去往敵國和親描滔。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,834評論 2 345

推薦閱讀更多精彩內(nèi)容