1. 程式人生 > >Android IPC 程序間通訊實現理解

Android IPC 程序間通訊實現理解

眾所周知,Android中的IPC(程序間通訊)採用了Binder機制,那麼要理解程序間通訊是如何實現的,理解Binder機制就相當關鍵了。

首先,為什麼Android的IPC要採用Binder機制呢,查閱資料,Binder機制的優點在於其少了一次拷貝過程,傳統的IPC需要將傳送端

傳送的資料從使用者空間copy到核心空間,在給到接受者時,再從核心空間copy給接受者,一次IPC請求需要兩次拷貝,而Binder機制

不同的時僅做一個copy操作,傳送IPC請求,會將Client端攜帶的資料copy到核心空間,放在共享記憶體中,通過核心與使用者空間的內

存對映到同一片物理空間,從而Server通過相關地址直接獲取到該共享記憶體中的相關資料,而不需要將其從核心空間copy到用

戶空間,這樣的機制提高了IPC的效能。

其次,Binder機制的組成:

1) 使用者空間: 

    Client: 發起IPC請求,通過從ServiceManager處獲取的Service程序Binder代理物件的引用,從而呼叫Service的相關方法(Service的服務介面可以通過AIDL檔案定義或者自定義介面類)

   Service:需要在ServiceManager中註冊,實現AIDL中的Stub類,其中包含提供的服務介面,Publish Service的Binder物件

   ServiceManager:負責管理系統中的Service,維護Service的Binder物件的引用,提供給Client以名稱方式查詢Service的服務介面以及Service

                                    的Binder物件引用

2) 核心空間: 

   Binder驅動:負責程序間的資料的傳遞,通過mmap記憶體對映實現共享記憶體, 連線 Client端與Server端

其實個人覺得Binder機制的原理大致瞭解一些就ok了,因為畢竟Binder機制大多數時候不需要去修改,而關鍵的是在理解Binder機制的基礎上

如何在Binder機制基礎上實現IPC,以及能夠看懂Android系統中的IPC實現,解決程序間通訊出現的問題。

如果從Code中去詳細瞭解Binder的具體實現,可以參考如下blog進行詳細的學習:

在Android Framework中有很多需要用到IPC的地方,比如startActivity, 在APP中呼叫startActivity這其實就發起了一次IPC請求,因為APP一般跑在

自己的程序中,而ActivityManagerSerive(AMS)執行在system_server程序中,這裡啟動Activity的請求其實就是一次IPC請求,APP程序將該請求發

送到AMS所在程序中,通過請求AMS的startActivity服務將想要啟動的activity啟動起來。

在實現IPC通訊的時候,每個程序都會定義自己的Binder類,並建立Binder物件,一般Binder實體物件存在於Server程序中,而Client程序想要通過

Binder機制進行服務請求,需要通過ServiceManager獲取一個Server程序中Binder代理物件的引用。

實現Binder物件很關鍵,也比較繁瑣,所以為了方便開發者進行IPC的開發,Android提供的AIDL(Android Interface Definition Language)介面定義語言,

ADT中的aidl工具會根據用AIDL書寫的XXX.aidl檔案自動生成對應的介面類,介面類繼承android.os.IInterface,其包含Stub和Proxy兩個內部類,它

們的作用分別是:

介面類: 例如IServiceAIDL.aidl,其生成的介面類為IServiceAIDL.java,繼承自android.os.IInterface, 包含在XXX.aidl檔案中定義的所有介面

Stub類: 供Server程序用,抽象類,繼承自android.os.Binder,implements IServiceAIDL介面,Server程序中Binder物件的型別,即Server程序的Code

             會去具體實現此Binder類並將其中的介面進行具體的實現,定義繼承自此類的子類或者直接匿名類實現對應的介面,此類中包含onTransact方法,這

           個方法會被Binder驅動回撥,根據傳入的介面程式碼(int代號),找到需要呼叫Server程序的哪個介面。

Proxy類: 供Client程序用,implements IServiceAIDL介面類,實現該介面類中的所有方法,其為Stub類的內部類,Server程序Binder類的代理物件,Client

              呼叫Proxy中的介面(即在XXX.aidl檔案中定義的服務介面),Proxy中對該介面的實現最終通過呼叫IBinder的transact方法進入到Binder驅動程式的實

             現流程中,Binder驅動處理完成即回撥Stub類的onTransact方法去呼叫Server的服務實現

這裡多說一句,為什麼要提定義介面呢,程序間進行通訊無非是想要用目標程序的服務,那麼為了使得程式鬆耦合,服務定義完介面就不再修改,

Client如果想使用某個服務,直接呼叫對應的介面就可以了,不用關心Server是如何實現的,相當與在Client和Server之間抽象出一層介面,Server定

義好介面之後,具體實現可以根據具體情況進行修改,不會影響到Client介面的使用。

AIDL方便了IInterface介面類的自動生成,讓開發者專注在服務介面的定義上,不用關注IInterface的繁瑣的細節實現,當然根據自己的需求完全可以自己寫

繼承自IInterface的介面類,不需要藉助AIDL工具。AIDL只是一個IPC的工具,跟IPC實現原理沒有關係,僅幫忙將aidl定義的介面生成符合Binder機制的如上

講解的三個重要類。下面分析分析IPC中這個必備的IInterface子類的實現方式:

1)通過aidl檔案方式自動生成IInterface的子類,以一個簡單的AIDL Demo來說明

      a. 僅關注介面定義服務端需要提供的介面,按照AIDL規則書寫XXX.aidl檔案

          IServiceAIDL.aidl

// IServiceAIDL.aidl
package com.xm.androiddemo;

//aidl僅支援一些接本的java型別,除了那些基本型別均必須import進來
import com.xm.androiddemo.IActivityAIDL;

// Declare any non-default types here with import statements

//Server 提供給 client 的介面檔案定義
//Service(Server) 暴露給 Activity(Client) 的介面定義在 aidl檔案中

interface IServiceAIDL {

    void callService();

    //在 Activity 中註冊 ActivityADIL 到Service中,使得 Service 可以回撥 Activity 中的方法
    void registActivityCallBack(IActivityAIDL callback);
}

     b. Eclipse或者Android Studio會自動根據如上aidl檔案生成IServiceAIDL.java檔案

         Eclipse生成的gen目錄下,Studio生成在$CodePath/app/build/generated/source/aidl/

          debug/com/xm/androiddemo/IServiceAIDL.java

         同樣你可以自己執行aidl工具來生成.aidl檔案對應的java檔案: aidl IServiceAIDL.aidl $outputPath

         具體的Code我就不貼了,自行在IServiceAIDL.java中找到IServiceAIDL, Stub, Proxy三個類,

         仔細看看理解一下

    c. Server程序中需要提供服務的Code中實現Stub類中的介面

    d.Client程序中通過建立IServiceAIDL物件來呼叫aidl檔案中定義的服務介面

   Demo的細節參考如下兩個例子:

2)開發者自己寫IInterface子類,定義必要的介面,以ActivityManagerService為例來說明

      IPC Binder機制涉及的三個類對應名字:

      介面類:IActivityManager.java            包含所有ActivityManagerService的提供的服務介面以及介面程式碼(數字代號)

      Stub類:ActivityManagerNative.java  繼承自Binder並implements IActivityManager介面類

      Proxy類:ActivityManagerProxy.java  ActivityManagerNative的內部類,implements IActivityManager介面類

     Client與Server對以上各類的實現:

     Client程序: Instrumentation類通過ActivityManagerNative.getDefault()獲取到IActivityManager介面物件,由於AMS

                     屬於遠端程序因此queryLocalInterface應該返回null,從而new一個ActivityManagerProxy物件,傳入AMS

                     程序的Binder物件引用,呼叫ActivityManagerProxy的startActivity方法,在startActivity的實現主要將IPC

                     需要傳輸的資料封裝成Parcel物件,然後呼叫Binder的transact方法,進入Binder驅動流程。

    Server程序: ActivityManagerService繼承自ActivityManagerNative,也即ActivityManagerService本身為本程序的Binder

                    型別,實現IActivityManager定義的服務介面。接著上面Client的startActivity執行流程,進入Binder驅動流程

                    之後,底層驅動處理完畢之後回撥上層ActivityManagerNative的onTransact方法,根據傳入的介面代號呼叫

                    ActivityManagerService的實現的startActivity

    印證如上的分析,簡單添加了幾句log,列印順序同上面分析一致

    01-01 08:08:16.112 D/ipclog ( 1817): ActivityManagerProxy transact START_ACTIVITY_TRANSACTION   
    01-01 08:08:16.122 D/ipclog (  596): ActivityManagerNative onTransact START_ACTIVITY_TRANSACTION
    01-01 08:08:16.122 D/ipclog (  596): ActivityManagerService startActivity

   如上log資訊可以看兩個程序號1817和596,596為AMS所在Server程序,1817為應用程序

   應用程序transact一個IPC請求之後,Binder驅動通過onTransact回撥到Server程序呼叫服務介面的實現。

 IPC的內容暫且分析到這裡,總的來說對這個流程有了個初步的認識,深層次的東西回頭再學習補充。。。

參考

1.http://blog.csdn.net/linmiansheng/article/details/42438813