Binder相關面試總結(四):一次Binder通信的基本流程是什么樣?

概述

AIDL (Android Interface Definition Language) 是一種接口定義語言,用于生成可以在Android設備上兩個進程之間進行PC的代碼。如果在一個進程中(例如Activity)要調用另一個進程中(例如Service)對象的操作,就可以使用AIDL生成可序列化的參數,來完成進程間通信,也就是說,為了簡化應用層進行IPC操作,Android提供了AIDL這門語言,在學習AIDL這門語言之前,我們來看一下Android中IPC的具體流程。

image
  • Client發起一個請求,阻塞
  • Client拿到服務端的Proxy,調用Proxy的相應方法
  • Proxy去跟Server進行交互,請求相應的結果
  • Proxy拿到結果返回給客戶端

Client跟Proxy進行交互的數據不需要進行序列化,但是Proxy跟Server進行交互的時候必須進行序列化跟反序列化

其實我們在實際進行IPC的時候并不需要關注Proxy以及Binder Driver,上述模型可以再次進行抽象

image

client端:BpBinder.transact()來發送事務請求;
server端:BBinder.onTransact()會接收到相應事務。

正文

數據類型

AIDL默認支持如下數據類型:

  • 基本數據類型
  • List集合
  • Map集合
  • String類型
  • CharSequence類型

如果定義的類型不是AIDL默認支持的類型,則需要使用Parcelable進行序列化。一類是用來定義parcelable對象,以供其他AIDL文件使用AIDL中非默認支持的數據類型的。

文件類型

AIDL的文件后綴名為.aidl,并不是之前的.java,不過在使用AIDL進行通信的過程中,
aidl文件只是用來定義數據類型跟接口。

使用方法

創建一個Parcelable 對象
public class People implements Parcelable {
    private int age;
    private String gender;
    private String hobby;
    //此處省略若干行代碼
}

創建一個AIDL的類
package com.wustor.aidl;
// Declare any non-default types here with import statements
parcelable People;

創建一個AIDL的接口
package com.wustor.aidl;
// Declare any non-default types here with import statements
import com.wustor.aidl.People;
interface PeopleManager {
   List<People> getPeople();
  void addPeople(in People people);
}

查看PeopleManager.java

Android系統會在".\app\build\generated\source\aidl\debug\com\wustor\aidl"的目錄下生成一個PeopleManager.java文件,這個類就是AIDL的核心,這個類是一個接口,下面先看一下這個接口的結構圖:

image

PeopleManager內部實現了PeopleManager.aidl的兩個方法,并且在內部創建了一個叫Stub的內部類,同時Stub也自己維護了一個叫做Proxy的內部類,通過前面對Binder機制的原理分析,我們其實可以很明確的知道,Proxy就是服務端的代理類,他作為一個 中間代理,承載了Client與Server之間的轉化,而Stub類的onTransact方法就是用來接收Proxy的輸入并且把請求結果返回,從而達到代理的作用,所以分析AIDL實際上只需要注重分析一下asInterface,onTransact這兩個方法以及Proxy代理類即可。

asBinder
    @Override
        public android.os.IBinder asBinder() {
            return this;
        }

返回當前Binder對象

asInterface

此方法位于Client端

 /**
         * Cast an IBinder object into an com.wustor.aidl.PeopleManager interface,
         * generating a proxy if needed.
         */
        public static com.wustor.aidl.PeopleManager asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            //查看本地是否存在這個類,不存在的話就重新創建
            if (((iin != null) && (iin instanceof com.wustor.aidl.PeopleManager))) {
                return ((com.wustor.aidl.PeopleManager) iin);
            }
            return new com.wustor.aidl.PeopleManager.Stub.Proxy(obj);
        }

queryLocalInterface

  public IInterface queryLocalInterface(String descriptor) {
        if (mDescriptor.equals(descriptor)) {
            return mOwner;
        }
        return null;
    }

    public void attachInterface(IInterface owner, String descriptor) {
        mOwner = owner;
        mDescriptor = descriptor;
    }

首先會調用queryLocalInterface來判斷descriptor跟mDescriptor是否相等,我們通過查看Binder的源碼可以看到descriptor就是當前PeopleManager的類名,那么通過傳遞過來的Binder對象查詢這個類名,實際上就是判斷Server端的BookManager跟Client是不是相同,如果相同就說明Client跟Server是在同一個進程,如果在同一個進程,那么就直接返回當前的IInterface ,否則返回null,那么Client端就會自己創建一個Proxy的代理類。

Proxy

getPeople()

    static final int TRANSACTION_getPeople = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    static final int TRANSACTION_addPeople = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
 @Override
  public java.util.List<com.wustor.aidl.People> getPeople() throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.util.List<com.wustor.aidl.People> _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    //調用transact方法,傳遞參數
                    mRemote.transact(Stub.TRANSACTION_getPeople, _data, _reply, 0);
                    _reply.readException();
                    //拿到返回結果
                    _result = _reply.createTypedArrayList(com.wustor.aidl.People.CREATOR);
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                //返回參數給Client
                return _result;
            }

在Client調用,執行Transact方法,當前線程阻塞,服務端的onTransact方法會被調用,從reply中拿到返回值后,線程繼續執行。

addPeoplet

 @Override
            public void addPeople(com.wustor.aidl.People people) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    if ((people != null)) {
                        _data.writeInt(1);
                        people.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                    mRemote.transact(Stub.TRANSACTION_addPeople, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }

在Client執行,跟getPeople()方法基本一致,唯一的區別是此方法沒有返回值,所以不需要回寫Client

onTransact
  @Override
        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(DESCRIPTOR);
                    return true;
                }
                case TRANSACTION_getPeople: {
                    data.enforceInterface(DESCRIPTOR);
                    java.util.List<com.wustor.aidl.People> _result = this.getPeople();
                    reply.writeNoException();
                    reply.writeTypedList(_result);
                    return true;
                }
                case TRANSACTION_addPeople: {
                    data.enforceInterface(DESCRIPTOR);
                    com.wustor.aidl.People _arg0;
                    if ((0 != data.readInt())) {
                        _arg0 = com.wustor.aidl.People.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    this.addPeople(_arg0);
                    reply.writeNoException();
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

運行在Server端,當Client端發起跨進程請求的時候,系統底層會返回Proxy代理,然后通過代理執行Client的方法,就會調用此方法。注意這個方法會從data取傳遞過來的參數,然后通過code判斷需要執行哪一個方法,執行完畢后,就返回值寫入reply中,此方法的返回值表明IPC請求是否成功。

總結

通過分析AIDL的原理,從而可以進一步理解Binder機制,總結一下,Android中利用Binder機制。

image

通過觀察這張圖,我們可以AIDL底層對Binder機制進行了封裝,讓Android中的IPC通信機制更加簡單方便,當然,我們也可以自己動手寫,當然Android中還有別的很多IPC通信方式,例如Messenger等,。如果想實現Binder機制進行通信,只需要Server(在Android里面大多數使用Service來創建一個Server)端返回一個Binder對象,然后將調用asInterface將Binder對象傳遞過來即可。

參考資料

Android開發藝術探索

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,527評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,687評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 178,640評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,957評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,682評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,011評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,009評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,183評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,714評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,435評論 3 359
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,665評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,148評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,838評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,251評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,588評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,379評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,627評論 2 380

推薦閱讀更多精彩內容