Binder通訊機制

beluga發表於2020-09-14

Binder學習總結



一、Binder概述

binder是Android中程式間通訊的機制。在Android framework層,binder是servicemanager連線各種manager和相應的managerservice的橋樑。對於Android應用層角度來說,binder是客戶端和服務端進行通訊的媒介。
在這裡插入圖片描述

二、Linux系統中的IPC原理

Android系統是基於linux核心的,瞭解linux ipc相關的概念有助於我們瞭解binder機制。

2.1基本知識介紹

2.1.1 程式隔離

在linux作業系統中,程式擁有自己的記憶體空間,各個程式的記憶體空間是互相隔離的,兩個程式要進行資料互動必須採用程式間的通訊機制即ipc。

2.1.1 程式空間劃分以及系統呼叫

在Android系統中,採用的是虛擬記憶體管理技術,每個程式都有各自互不干涉的程式地址空間,該空間是虛擬空間。程式地址空間被分為兩個部分,分別是使用者空間和核心空間,使用者程式通常情況下只能訪問到使用者空間的虛擬地址,只有使用者程式進行系統呼叫等時刻可以訪問到核心空間。每當程式進行切換時,使用者空間會跟著變換,而核心空間不會隨著程式改變。
系統呼叫時使用者程式訪問核心空間的方式,其保證了系統資源的訪問都是在核心的控制下進行的,避免了使用者程式對系統資源的越權訪問,提升了系統的安全性和穩定性。進行執行使用者空間的程式碼時處於 使用者態,當進行執行系統呼叫時稱程式處於 核心態
在這裡插入圖片描述

2.2 linux ipc原理

2.2.1 linux程式間通訊的方式

pipe管道及FIFO有名管道

管道是linux支援的最初的ipc形式之一,具有以下特點:

  • 管道是半雙工的,資料只能向一個方向流動;需要雙方通訊時,需要建立起兩個管道
  • 只能用於父子程式或者兄弟程式之間(具有親緣關係的程式)
  • 單獨構成一種獨立的檔案系統:管道對於管道兩端的程式而言,就是一個檔案,但它不是普通的檔案,它不屬於某種檔案系統,而是自立門戶,單獨構成一種檔案系統,並且只存在與記憶體中
  • 資料的讀出和寫入:一個程式向管道中寫的內容被管道另一端的程式讀出。寫入的內容每次都新增在管道緩衝區的末尾,並且每次都是從緩衝區的頭部讀出資料。
  • 管道的緩衝區是有限的(管道制存在於記憶體中,在管道建立時,為緩衝區分配一個頁面大小)
  • 管道所傳送的是無格式位元組流,這就要求管道的讀出方和寫入方必須事先約定好資料的格式,比如多少位元組算作一個訊息(或命令、或記錄)等等
    管道應用的一個重大限制是它沒有名字,因此,只能用於具有親緣關係的程式間通訊,在有名管道(named pipe或FIFO)提出後,該限制得到了克服。FIFO不同於管道之處在於它提供一個路徑名與之關聯,以FIFO的檔案形式存在於檔案系統中
訊號

可參考文章:
http://www.ibm.com/developerworks/cn/linux/l-ipc/part2/index1.html
http://www.ibm.com/developerworks/cn/linux/l-ipc/part2/index2.html

訊息佇列

訊息佇列就是一個訊息的連結串列。可以把訊息看作一個記錄,具有特定的格式以及特定的優先順序。對訊息佇列有寫許可權的程式可以向中按照一定的規則新增新訊息;對訊息佇列有讀許可權的程式則可以從訊息佇列中讀走訊息。訊息佇列與管道以及有名管道相比,具有更大的靈活性,首先,它提供有格式位元組流,有利於減少開發人員的工作量;其次,訊息具有型別,在實際應用中,可作為優先順序使用。這兩點是管道以及有名管道所不能比的。同樣,訊息佇列可以在幾個程式間複用,而不管這幾個程式是否具有親緣關係,這一點與有名管道很相似;但訊息佇列是隨核心持續的,與有名管道(隨程式持續)相比,生命力更強,應用空間更大。

共享記憶體

使得多個程式可以訪問同一塊記憶體空間,是最快的可用IPC形式。是針對其他通訊機制執行效率較低而設計的。往往與其它通訊機制,如訊號量結合使用,來達到程式間的同步及互斥。採用共享記憶體通訊的一個顯而易見的好處是效率高,因為程式可以直接讀寫記憶體,而不需要任何資料的複製。對於像管道和訊息佇列等通訊方式,則需要在核心和使用者空間進行四次的資料複製,而共享記憶體則只複製兩次資料[1]:一次從輸入檔案到共享記憶體區,另一次從共享記憶體區到輸出檔案。實際上,程式之間在共享記憶體時,並不總是讀寫少量資料後就解除對映,有新的通訊時,再重新建立共享記憶體區域。而是保持共享區域,直到通訊完畢為止,這樣,資料內容一直儲存在共享記憶體中,並沒有寫回檔案。共享記憶體中的內容往往是在解除對映時才寫回檔案的。因此,採用共享記憶體的通訊方式效率是非常高的。
Linux的2.2.x核心支援多種共享記憶體方式,如mmap()系統呼叫,Posix共享記憶體,以及系統V共享記憶體。mmap()系統呼叫使得程式之間透過對映同一個普通檔案實現共享記憶體。普通檔案被對映到程式地址空間後,程式可以向訪問普通記憶體一樣對檔案進行訪問,不必再呼叫read(),write()等操作

socket

更為一般的程式間通訊機制,可用於不同機器之間的程式間通訊
這些傳統的 IPC 通訊方式有兩個問題:

  • 效能低下,一次資料傳遞需要經歷:記憶體快取區 --> 核心快取區 --> 記憶體快取區,需要 2 次資料複製;
  • 接收資料的快取區由資料接收程式提供,但是接收程式並不知道需要多大的空間來存放將要傳遞過來的資料,因此只能開闢儘可能大的記憶體空間或者先呼叫 API 接收訊息頭來獲取訊息體的大小,這兩種做法不是浪費空間就是浪費時間。

三、Binder 跨程式通訊原理

3.1動態核心可載入模組

跨程式通訊需要核心空間來實現,Linux的 動態核心可載入模組的機制(Loadable Kernel Module,LKM),binder驅動模組是具有獨立功能的程式,可以被單獨編譯,但是不能獨立執行。其在執行的時候被連結到核心作為核心的一部分執行。Android系統透過動態新增一個binder模型執行在核心空間,使用者程式之間透過這個binder核心模組作為橋樑實現通訊。
binder機制是透過記憶體對映即mmap() 來實現,記憶體對映簡單的講就是將使用者空間的一塊記憶體區域對映到核心空間。對映關係建立後,使用者對這塊記憶體區域的修改可以直接反應到核心空間;反之核心空間對這段區域的修改也能直接反應到使用者空間。記憶體對映能減少資料複製次數,實現使用者空間和核心空間的高效互動。

3.1Binder IPC 實現原理

Binder IPC 正是基於記憶體對映(mmap)來實現的,但是 mmap() 通常是用在有物理介質的檔案系統上的。Binder 驅動使用 mmap() 是用來在核心空間建立資料接收的快取空間。
一次完整的 Binder IPC 通訊過程通常是這樣:

  • 首先 Binder 驅動在核心空間建立一個資料接收快取區。
  • 接著在核心空間開闢一塊核心快取區,建立核心快取區和核心中資料接收快取區之間的對映關係,以及核心中快取區和接收程式使用者空間地址的對映關係;
  • 傳送方程式透過系統呼叫 copy_from_user() 將資料 copy 到核心中的核心快取區,由於核心快取區和接收程式的使用者空間存在記憶體對映,因此也就相當於把資料傳送到了接收程式的使用者空間,這樣便完成了一次程式間的通訊。
    在這裡插入圖片描述

四、Binder 通訊模型

一次完整的程式間通訊必然至少包含兩個程式,通常我們稱通訊的雙方分別為客戶端程式(Client)和服務端程式(Server)

4.1.Client/Server/ServiceManager/驅動

Binder 是基於 C/S 架構的。由一系列的元件組成,包括 Client、Server、ServiceManager、Binder 驅動。其中 Client、Server、Service Manager 執行在使用者空間,Binder 驅動執行在核心空間。其中 Service Manager 和 Binder 驅動由系統提供,而 Client、Server 由應用程式來實現。
在這裡插入圖片描述
Client、Server 和 ServiceManager 均是透過系統呼叫 open、mmap 和 ioctl 來訪問裝置檔案 /dev/binder,從而實現與 Binder 驅動的互動來間接的實現跨程式通訊

4.1.1Binder 驅動

Binder 驅動就如同路由器一樣,是整個通訊的核心;驅動負責程式之間 Binder 通訊的建立,Binder 在程式之間的傳遞,Binder 引用計數管理,資料包在程式之間的傳遞和互動等一系列底層支援

4.1.2 ServiceManager 與實名 Binder

ServiceManager 和 DNS 類似,作用是將字元形式的 Binder 名字轉化成 Client 中對該 Binder 的引用,使得 Client 能夠透過 Binder 的名字獲得對 Binder 實體的引用。註冊了名字的 Binder 叫實名 Binder,就像網站一樣除了除了有 IP 地址意外還有自己的網址。Server 建立了 Binder,併為它起一個字元形式,可讀易記得名字,將這個 Binder 實體連同名字一起以資料包的形式透過 Binder 驅動傳送給 ServiceManager ,通知 ServiceManager 註冊一個名為“張三”的 Binder,它位於某個 Server 中。驅動為這個穿越程式邊界的 Binder 建立位於核心中的實體節點以及 ServiceManager 對實體的引用,將名字以及新建的引用打包傳給 ServiceManager。ServiceManger 收到資料後從中取出名字和引用填入查詢表。ServierManager 是一個程式,Server 是另一個程式,Server 向 ServiceManager 中註冊 Binder 必然涉及到程式間通訊。當前實現程式間通訊又要用到程式間通訊

4.1.3 Client 獲得實名 Binder 的引用

Server 向 ServiceManager 中註冊了 Binder 以後, Client 就能透過名字獲得 Binder 的引用了。Client 也利用保留的 0 號引用向 ServiceManager 請求訪問某個 Binder: 我申請訪問名字叫張三的 Binder 引用。ServiceManager 收到這個請求後從請求資料包中取出 Binder 名稱,在查詢表裡找到對應的條目,取出對應的 Binder 引用作為回覆傳送給發起請求的 Client。從物件導向的角度看,Server 中的 Binder 實體現在有兩個引用:一個位於 ServiceManager 中,一個位於發起請求的 Client 中。如果接下來有更多的 Client 請求該 Binder,系統中就會有更多的引用指向該 Binder ,就像 Java 中一個物件有多個引用一樣。

4.2 Binder 通訊過程

  • 首先,一個程式使用 BINDER_SET_CONTEXT_MGR 命令透過 Binder 驅動將自己註冊成為 ServiceManager;
  • Server 透過驅動向 ServiceManager 中註冊 Binder(Server 中的 Binder 實體),表明可以對外提供服務。驅動為這個 Binder 建立位於核心中的實體節點以及 ServiceManager 對實體的引用,將名字以及新建的引用打包傳給 ServiceManager,ServiceManger 將其填入查詢表。
  • Client 透過名字,在 Binder 驅動的幫助下從 ServiceManager 中獲取到對 Binder 實體的引用,透過這個引用就能實現和 Server 程式的通訊
    在這裡插入圖片描述

4.3 Binder 通訊中的代理模式

當 A 程式想要獲取 B 程式中的 object 時,驅動並不會真的把 object 返回給 A,而是返回了一個跟 object 看起來一模一樣的代理物件 objectProxy,這個 objectProxy 具有和 object 一摸一樣的方法,但是這些方法並沒有 B 程式中 object 物件那些方法的能力,這些方法只需要把把請求引數交給驅動即可。對於 A 程式來說和直接呼叫 object 中的方法是一樣的。當 Binder 驅動接收到 A 程式的訊息後,發現這是個 objectProxy 就去查詢自己維護的表單,一查發現這是 B 程式 object 的代理物件。於是就會去通知 B 程式呼叫 object 的方法,並要求 B 程式把返回結果發給自己。當驅動拿到 B 程式的返回結果後就會轉發給 A 程式,一次通訊就完成了

五、AIDL編碼實現跨程式呼叫

5.1 各 Java 類職責描述

  • IBinder : IBinder 是一個介面,代表了一種跨程式通訊的能力。只要實現了這個藉口,這個物件就能跨程式傳輸。
  • IInterface : IInterface 代表的就是 Server 程式物件具備什麼樣的能力(能提供哪些方法,其實對應的就是 AIDL 檔案中定義的介面)
  • Binder : Java 層的 Binder 類,代表的其實就是 Binder 本地物件。BinderProxy 類是 Binder 類的一個內部類,它代表遠端程式的 Binder 物件的本地代理;這兩個類都繼承自 IBinder, 因而都具有跨程式傳輸的能力;實際上,在跨越程式的時候,Binder 驅動會自動完成這兩個物件的轉換。
  • Stub : AIDL 的時候,編譯工具會給我們生成一個名為 Stub 的靜態內部類;這個類繼承了 Binder, 說明它是一個 Binder 本地物件,它實現了 IInterface 介面,表明它具有 Server 承諾給 Client 的能力;Stub 是一個抽象類,具體的 IInterface 的相關實現需要開發者自己實現。

5.2 AIDL實現過程

5.2.1 建立一個aidl檔案在裡面定義一個介面,介面繼承自IInterface,裡面定義服務端提供給客戶端的方法
interface IHelloService {
   void sayHello();
   int sayHello_to(String name);}1234
5.2.2 透過Android studio編譯aidl檔案,編譯後生成對應的java檔案
package com.example.binderdemo;// Declare any non-default types here with import statementspublic interface IHelloService extends android.os.IInterface{
  /** Default implementation for IHelloService. */
  public static class Default implements com.example.binderdemo.IHelloService  {
    @Override public void sayHello() throws android.os.RemoteException    {
    }
    @Override public int sayHello_to(java.lang.String name) throws android.os.RemoteException    {
      return 0;
    }
    @Override
    public android.os.IBinder asBinder() {
      return null;
    }
  }
  /** Local-side IPC implementation stub class. */
  public static abstract class Stub extends android.os.Binder implements com.example.binderdemo.IHelloService  {
    private static final java.lang.String DESCRIPTOR = "com.example.binderdemo.IHelloService";
    /** Construct the stub at attach it to the interface. */
    public Stub()
    {
      this.attachInterface(this, DESCRIPTOR);
    }
    /**
     * Cast an IBinder object into an com.example.binderdemo.IHelloService interface,
     * generating a proxy if needed.
     */
    public static com.example.binderdemo.IHelloService asInterface(android.os.IBinder obj)
    {
      if ((obj==null)) {
        return null;
      }
      android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
      if (((iin!=null)&&(iin instanceof com.example.binderdemo.IHelloService))) {
        return ((com.example.binderdemo.IHelloService)iin);
      }
      return new com.example.binderdemo.IHelloService.Stub.Proxy(obj);
    }
    @Override public android.os.IBinder asBinder()
    {
      return this;
    }
    @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException    {
      java.lang.String descriptor = DESCRIPTOR;
      switch (code)
      {
        case INTERFACE_TRANSACTION:
        {
          reply.writeString(descriptor);
          return true;
        }
        case TRANSACTION_sayHello:
        {
          data.enforceInterface(descriptor);
          this.sayHello();
          reply.writeNoException();
          return true;
        }
        case TRANSACTION_sayHello_to:
        {
          data.enforceInterface(descriptor);
          java.lang.String _arg0;
          _arg0 = data.readString();
          int _result = this.sayHello_to(_arg0);
          reply.writeNoException();
          reply.writeInt(_result);
          return true;
        }
        default:
        {
          return super.onTransact(code, data, reply, flags);
        }
      }
    }
    private static class Proxy implements com.example.binderdemo.IHelloService    {
      private android.os.IBinder mRemote;
      Proxy(android.os.IBinder remote)
      {
        mRemote = remote;
      }
      @Override public android.os.IBinder asBinder()
      {
        return mRemote;
      }
      public java.lang.String getInterfaceDescriptor()
      {
        return DESCRIPTOR;
      }
      @Override public void sayHello() throws android.os.RemoteException      {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        try {
          _data.writeInterfaceToken(DESCRIPTOR);
          boolean _status = mRemote.transact(Stub.TRANSACTION_sayHello, _data, _reply, 0);
          if (!_status && getDefaultImpl() != null) {
            getDefaultImpl().sayHello();
            return;
          }
          _reply.readException();
        }
        finally {
          _reply.recycle();
          _data.recycle();
        }
      }
      @Override public int sayHello_to(java.lang.String name) throws android.os.RemoteException      {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        int _result;
        try {
          _data.writeInterfaceToken(DESCRIPTOR);
          _data.writeString(name);
          boolean _status = mRemote.transact(Stub.TRANSACTION_sayHello_to, _data, _reply, 0);
          if (!_status && getDefaultImpl() != null) {
            return getDefaultImpl().sayHello_to(name);
          }
          _reply.readException();
          _result = _reply.readInt();
        }
        finally {
          _reply.recycle();
          _data.recycle();
        }
        return _result;
      }
      public static com.example.binderdemo.IHelloService sDefaultImpl;
    }
    static final int TRANSACTION_sayHello = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    static final int TRANSACTION_sayHello_to = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    public static boolean setDefaultImpl(com.example.binderdemo.IHelloService impl) {
      // Only one user of this interface can use this function
      // at a time. This is a heuristic to detect if two different
      // users in the same process use this function.
      if (Stub.Proxy.sDefaultImpl != null) {
        throw new IllegalStateException("setDefaultImpl() called twice");
      }
      if (impl != null) {
        Stub.Proxy.sDefaultImpl = impl;
        return true;
      }
      return false;
    }
    public static com.example.binderdemo.IHelloService getDefaultImpl() {
      return Stub.Proxy.sDefaultImpl;
    }
  }
  public void sayHello() throws android.os.RemoteException;
  public int sayHello_to(java.lang.String name) throws android.os.RemoteException;}123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159

這個java類首先宣告瞭AIDL檔案中定義的方法,同時宣告瞭兩個整形id用來標識這兩個方法。接著宣告瞭一個內部類Stub,這個Stub是一個binder類,
還定義了一個stub的內部代理類proxy。

public static abstract class Stub extends android.os.Binder implements com.example.binderdemo.IHelloService  {
  //binder的唯一標識,一般用當前binder的類名錶示
    private static final java.lang.String DESCRIPTOR = "com.example.binderdemo.IHelloService";
    /** Construct the stub at attach it to the interface. */
    public Stub()
    {
      this.attachInterface(this, DESCRIPTOR);
    }
    /**
     * Cast an IBinder object into an com.example.binderdemo.IHelloService interface,
     * generating a proxy if needed.
     */
     //該方法用於將服務端的binder物件轉換為客戶端所需AIDL介面型別的物件,這種轉換過程區分程式,如果客戶端和服務端位於同一程式,此方法返回的就是服務端的stub物件本身,否則返回的是系統封裝後的stub.proxy物件
    public static com.example.binderdemo.IHelloService asInterface(android.os.IBinder obj)
    {
      if ((obj==null)) {
        return null;
      }
      android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
      if (((iin!=null)&&(iin instanceof com.example.binderdemo.IHelloService))) {
        return ((com.example.binderdemo.IHelloService)iin);
      }
      return new com.example.binderdemo.IHelloService.Stub.Proxy(obj);
    }
    //此方法用於返回當前binder物件
    @Override public android.os.IBinder asBinder()
    {
      return this;
    }
    //該方法執行在服務端中的binder執行緒池中,當客戶端發起跨程式請求時,遠端請求透過系統底層封裝後交給此方法處理。服務端透過code引數確定客戶端所請求的目標方法是什麼,接著從data中取出目標方法所需的引數(如果目標方法需要引數),然後執行目標方法,當目標方法執行完畢以後,就向reply中寫入返回值(目標方法存在返回值),當此方法返回false時,表示客戶端的請求失敗,用此特徵可以做許可權驗證。
    @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException    {
      java.lang.String descriptor = DESCRIPTOR;
      switch (code)
      {
        case INTERFACE_TRANSACTION:
        {
          reply.writeString(descriptor);
          return true;
        }
        case TRANSACTION_sayHello:
        {
          data.enforceInterface(descriptor);
          this.sayHello();
          reply.writeNoException();
          return true;
        }
        case TRANSACTION_sayHello_to:
        {
          data.enforceInterface(descriptor);
          java.lang.String _arg0;
          _arg0 = data.readString();
          int _result = this.sayHello_to(_arg0);
          reply.writeNoException();
          reply.writeInt(_result);
          return true;
        }
        default:
        {
          return super.onTransact(code, data, reply, flags);
        }
      }
    }
    //stub的代理類
    private static class Proxy implements com.example.binderdemo.IHelloService    {
      private android.os.IBinder mRemote;
      Proxy(android.os.IBinder remote)
      {
        mRemote = remote;
      }
      @Override public android.os.IBinder asBinder()
      {
        return mRemote;
      }
      public java.lang.String getInterfaceDescriptor()
      {
        return DESCRIPTOR;
      }
      //這個方法執行在客戶端,當客戶端遠端呼叫此方法時,它的內部實現是1.首先建立該方法所需要的引數資訊寫入到_data中,接著呼叫transact方法來發起RPC(遠端過程呼叫)請求,同時當前執行緒掛起,然後服務端onTransact方法會被呼叫,直到RPC過程返回後,當前執行緒繼承執行,並從_reply中取出rpc過程的返回結果,最後返回_reply中的資料
      @Override public void sayHello() throws android.os.RemoteException      {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        try {
          _data.writeInterfaceToken(DESCRIPTOR);
          boolean _status = mRemote.transact(Stub.TRANSACTION_sayHello, _data, _reply, 0);
          if (!_status && getDefaultImpl() != null) {
            getDefaultImpl().sayHello();
            return;
          }
          _reply.readException();
        }
        finally {
          _reply.recycle();
          _data.recycle();
        }
      }
      @Override public int sayHello_to(java.lang.String name) throws android.os.RemoteException      {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        int _result;
        try {
          _data.writeInterfaceToken(DESCRIPTOR);
          _data.writeString(name);
          boolean _status = mRemote.transact(Stub.TRANSACTION_sayHello_to, _data, _reply, 0);
          if (!_status && getDefaultImpl() != null) {
            return getDefaultImpl().sayHello_to(name);
          }
          _reply.readException();
          _result = _reply.readInt();
        }
        finally {
          _reply.recycle();
          _data.recycle();
        }
        return _result;
      }
      public static com.example.binderdemo.IHelloService sDefaultImpl;
    }
    static final int TRANSACTION_sayHello = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    static final int TRANSACTION_sayHello_to = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    public static boolean setDefaultImpl(com.example.binderdemo.IHelloService impl) {
      // Only one user of this interface can use this function
      // at a time. This is a heuristic to detect if two different
      // users in the same process use this function.
      if (Stub.Proxy.sDefaultImpl != null) {
        throw new IllegalStateException("setDefaultImpl() called twice");
      }
      if (impl != null) {
        Stub.Proxy.sDefaultImpl = impl;
        return true;
      }
      return false;
    }
    public static com.example.binderdemo.IHelloService getDefaultImpl() {
      return Stub.Proxy.sDefaultImpl;
    }
  }123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141

總結:

  • 檔案裡面包括了一個跨程式呼叫物件 Stub。Stub 繼承 Binder, 說明它是一個 Binder 本地物件;實現 IInterface 介面,表明具有 Server 承諾給 Client 的能力;Stub 是一個抽象類,具體的 IInterface 的相關實現需要呼叫方自己實現
  • 在Stub 類中具有兩個方法 asInterface 和 onTransact。asInterface,當 Client 端在建立和服務端的連線,呼叫 bindService 時需要建立一個 ServiceConnection 物件作為入參。在 ServiceConnection 的回撥方法 onServiceConnected 中 會透過這個 asInterface(IBinder binder) 拿到 aidl 物件,這個 IBinder 型別的入參 binder 是驅動傳給我們的,方法中會去呼叫 binder.queryLocalInterface() 去查詢 Binder 本地物件,如果找到了就說明 Client 和 Server 在同一程式,那麼這個 binder 本身就是 Binder 本地物件,可以直接使用。否則說明是 binder 是個遠端物件,也就是 BinderProxy。因此需要我們建立一個代理物件 Proxy,透過這個代理物件來是實現遠端訪問。在java檔案中也定義了代理物件 Proxy。正如前文所述 Proxy 是在 Stub 的 asInterface 中建立,能走到建立 Proxy 這一步就說明 Proxy 建構函式的入參是 BinderProxy,即這裡的 remote 是個 BinderProxy 物件。最終透過一系列的函式呼叫,Client 程式透過系統呼叫陷入核心態,Client 程式中執行 addBook() 的執行緒掛起等待返回;驅動完成一系列的操作之後喚醒 Server 程式,呼叫 Server 程式本地物件的 onTransact()。最終又走到了 Stub 中的 onTransact() 中,onTransact() 根據函式編號呼叫相關函式(在 Stub 類中為介面中的每個函式中定義了一個編號,只不過上面的原始碼中我們簡化掉了;在跨程式呼叫的時候,不會傳遞函式而是傳遞編號來指明要呼叫哪個函式);我們這個例子裡面,呼叫了 Binder 本地物件的 addBook() 並將結果返回給驅動,驅動喚醒 Client 程式裡剛剛掛起的執行緒並將結果返回。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69981976/viewspace-2719444/,如需轉載,請註明出處,否則將追究法律責任。

相關文章