AIDL原理及Binder基本流程分析

與Binder相關(guān)的幾個類

可以參考類圖刚照,這些類的職責(zé)都在類圖中寫出來了


Binder.png

Binder的流程圖

Binder流程.jpg

光看這兩張圖蒿秦,可能對這些依然沒有什么概念具练,下面結(jié)合一個具體的AIDL例子合住,來具體看下這些類帘饶、流程都是怎么工作的哑诊。


AIDL的原理

看下面一個例子,我們先定義一個aidl接口及刻,然后綁定服務(wù)镀裤,在client中獲取該接口的實(shí)例:

// IMyAidlInterface.aidl
package com.example.myapplication.aidl;

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

interface IMyAidlInterface {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void myTest(int anInt);
}

在需要的地方綁定服務(wù):

private ServiceConnection connection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        // 我們這里拿到的對象其實(shí)就是其Stub的內(nèi)部類Proxy對象
        myAidlInterface = IMyAidlInterface.Stub.asInterface(service);
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {

    }
};

private void bindAIDLService() {
    Intent intent = new Intent();
    bindService(intent, connection, BIND_AUTO_CREATE);
}

下面看一下編譯之后系統(tǒng)自動生成的java類竞阐,我們結(jié)合上面的兩張圖來理解Binder類的各個職責(zé)及流程:


/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: D:\\Demo\\MyTest\\app\\src\\main\\aidl\\com\\example\\myapplication\\aidl\\IMyAidlInterface.aidl
 */
// Declare any non-default types here with import statements

public interface IMyAidlInterface extends android.os.IInterface {

    /** Local-side IPC implementation stub class. */
    public static abstract class Stub extends android.os.Binder implements com.example.myapplication.aidl.IMyAidlInterface {

        // 描述符
        private static final java.lang.String DESCRIPTOR = "com.example.myapplication.aidl.IMyAidlInterface";

        /** Construct the stub at attach it to the interface. */
        public Stub()
        {
            this.attachInterface(this, DESCRIPTOR);
        }

        /**
         * Cast an IBinder object into an com.example.myapplication.aidl.IMyAidlInterface interface,
         * generating a proxy if needed.
         * 這里就是我們在將IBinder轉(zhuǎn)化為具體的aidl接口時調(diào)用的方法
         */
        public static com.example.myapplication.aidl.IMyAidlInterface asInterface(android.os.IBinder obj) {
            if ((obj==null)) {
                return null;
            }

            // 先檢查本地是否有aidl對象,如果client和server在同一進(jìn)程暑劝,則會走進(jìn)這個回調(diào)
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin!=null)&&(iin instanceof com.example.myapplication.aidl.IMyAidlInterface))) {
                return ((com.example.myapplication.aidl.IMyAidlInterface)iin);
            }

            // 在不同進(jìn)程骆莹,就返回一個Proxy對象
            return new com.example.myapplication.aidl.IMyAidlInterface.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_myTest: {
                    data.enforceInterface(descriptor);
                    int _arg0;
                    _arg0 = data.readInt();
                    this.myTest(_arg0);
                    reply.writeNoException();
                    return true;
                }
                default: {
                    return super.onTransact(code, data, reply, flags);
                }
            }
        }


        // 其實(shí)是Server在Client中的本地代理對象,實(shí)現(xiàn)了在aidl中定義的方法担猛,其實(shí)是將參數(shù)序列化
        // 后幕垦,調(diào)用mRemote(也就是BinderProxy)去處理;一般情況下我們在綁定服務(wù)后傅联,拿到的遠(yuǎn)程對象先改,
        // 就是這個Proxy
        private static class Proxy implements com.example.myapplication.aidl.IMyAidlInterface {

            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;
            }

            /**
             * Demonstrates some basic types that you can use as parameters
             * and return values in AIDL.
             * 這個方法將參數(shù)序列化后,交給mRemote去處理蒸走,transact中傳入了每個參數(shù)對應(yīng)的編號仇奶;
             * 這時,client調(diào)用方法線程會掛起比驻,等待server響應(yīng);
             * 等服務(wù)端處理結(jié)束后该溯,將數(shù)據(jù)返回至binder驅(qū)動,后者喚醒掛起的線程别惦,這時一次跨進(jìn)程通信就完成了
             */
            @Override public void myTest(int anInt) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeInt(anInt);
                    mRemote.transact(Stub.TRANSACTION_myTest, _data, _reply, 0);
                    _reply.readException();
                }
                finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
        }

        static final int TRANSACTION_myTest = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    }


    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    public void myTest(int anInt) throws android.os.RemoteException;
}


可以看到狈茉,一共生成了三個類:

  • IMyAidlInterface extends android.os.IInterface,client中獲取到的是這個接口步咪,用于client和server通信
  • IMyAidlInterface.Stub
  • IMyAidlInterface.Stub.Proxy:server的本地代理接口论皆,我們調(diào)用的方法都是由它來處理益楼,傳遞給binder對象的

下面我們將上面三個類單獨(dú)拆分出來看下

首先是繼承自IInterface接口的IMyAidlInterface接口猾漫,只聲明了一個方法,也就是我們定義的aidl方法
所有可以在Binder中傳輸?shù)慕涌诙夹枰^承自IInterface接口
public interface IMyAidlInterface extends android.os.IInterface {

    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    public void myTest(int anInt) throws android.os.RemoteException;
}


/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.example.myapplication.aidl.IMyAidlInterface {

    // 描述符感凤,Binder的唯一標(biāo)識
    private static final java.lang.String DESCRIPTOR = "com.example.myapplication.aidl.IMyAidlInterface";
    
    static final int TRANSACTION_myTest = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);

    /** Construct the stub at attach it to the interface. */
    public Stub(){
        this.attachInterface(this, DESCRIPTOR);
    }

    /**
     * Cast an IBinder object into an com.example.myapplication.aidl.IMyAidlInterface interface,
     * generating a proxy if needed.
     * 這里就是我們在將IBinder轉(zhuǎn)化為具體的aidl接口時調(diào)用的方法
     */
    public static com.example.myapplication.aidl.IMyAidlInterface asInterface(android.os.IBinder obj) {
        if ((obj==null)) {
            return null;
        }

        // 先檢查本地是否有aidl對象悯周,如果client和server在同一進(jìn)程,則會走進(jìn)這個回調(diào)
        android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
        if (((iin!=null)&&(iin instanceof com.example.myapplication.aidl.IMyAidlInterface))) {
            return ((com.example.myapplication.aidl.IMyAidlInterface)iin);
        }

        // 在不同進(jìn)程陪竿,就返回一個Proxy對象
        return new com.example.myapplication.aidl.IMyAidlInterface.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_myTest: {
                data.enforceInterface(descriptor);
                int _arg0;
                _arg0 = data.readInt();
                this.myTest(_arg0);
                reply.writeNoException();
                return true;
            }
            default: {
                return super.onTransact(code, data, reply, flags);
            }
        }
    }

}

這里的Stub就是一個Binder的子類禽翼,當(dāng)客戶端和服務(wù)端都處于同一個進(jìn)程時,方法調(diào)用不會走跨進(jìn)程的transact過程族跛,只有二者在不同進(jìn)程時闰挡,才會走transact,該過程由Proxy類來完成

  • asInterface:用于將服務(wù)端的Binder對象轉(zhuǎn)化為客戶端所需的AIDL接口類型的對象礁哄;依然區(qū)分進(jìn)程长酗,如果是同進(jìn)程,那返回的是服務(wù)端的Stub本身桐绒,否則夺脾,返回的是封裝后的Stub.Proxy對象
  • asBinder:返回當(dāng)前Binder對象
  • onTransact:該方法運(yùn)行在服務(wù)端中的Binder線程池之拨,客戶端發(fā)起的遠(yuǎn)程請求會通過底層封裝后交給該方法來處理。服務(wù)端通過code參數(shù)可以確認(rèn)調(diào)用的是哪個方法咧叭,然后從data中取出參數(shù)蚀乔,執(zhí)行目標(biāo)方法; 當(dāng)目標(biāo)方法執(zhí)行完后菲茬,向reply中寫入返回值吉挣,再通過Binder返回給客戶端

// 其實(shí)是Server在Client中的本地代理對象,實(shí)現(xiàn)了在aidl中定義的方法婉弹,其實(shí)是將參數(shù)序列化
// 后听想,調(diào)用mRemote(也就是BinderProxy)去處理;一般情況下我們在綁定服務(wù)后马胧,拿到的遠(yuǎn)程對象汉买,
// 就是這個Proxy
private static class Proxy implements com.example.myapplication.aidl.IMyAidlInterface {

    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;
    }

    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     * 這個方法將參數(shù)序列化后,交給mRemote去處理佩脊,transact中傳入了每個參數(shù)對應(yīng)的編號蛙粘;
     * 這時,client調(diào)用方法線程會掛起威彰,等待server響應(yīng);
     * 等服務(wù)端處理結(jié)束后出牧,將數(shù)據(jù)返回至binder驅(qū)動,后者喚醒掛起的線程歇盼,這時一次跨進(jìn)程通信就完成了
     */
    @Override public void myTest(int anInt) throws android.os.RemoteException {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        try {
            _data.writeInterfaceToken(DESCRIPTOR);
            _data.writeInt(anInt);
            mRemote.transact(Stub.TRANSACTION_myTest, _data, _reply, 0);
            _reply.readException();
        }
        finally {
            _reply.recycle();
            _data.recycle();
        }
    }
}
  • myTest:該方法運(yùn)行在客戶端舔痕,客戶端調(diào)用時,首先創(chuàng)建輸入?yún)?shù)_data豹缀,將對應(yīng)的參數(shù)寫入 _data,然后調(diào)用transact()方法發(fā)起遠(yuǎn)程調(diào)用伯复,然后當(dāng)前線程會掛起,服務(wù)端的transact方法得到調(diào)用返回結(jié)果后邢笙,該線程繼續(xù)執(zhí)行啸如,從 _reply中取出返回值并返回

注意

  • 客戶端發(fā)起遠(yuǎn)程請求時,當(dāng)前線程會被掛起直到服務(wù)端返回?cái)?shù)據(jù)氮惯,如果一個遠(yuǎn)程方法可能是耗時的叮雳,那就不能在主線程中去發(fā)起調(diào)用
  • 服務(wù)端的Binder方法是運(yùn)行在Binder線程池中的,因此binder方法無論耗時與否妇汗,都應(yīng)該采用同步的方法去實(shí)現(xiàn)帘不,因?yàn)樗呀?jīng)運(yùn)行在一個子線程中了

參考:http://www.reibang.com/p/062a6e4f5cbe

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市杨箭,隨后出現(xiàn)的幾起案子寞焙,更是在濱河造成了極大的恐慌,老刑警劉巖告唆,帶你破解...
    沈念sama閱讀 216,496評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件棺弊,死亡現(xiàn)場離奇詭異晶密,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)模她,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評論 3 392
  • 文/潘曉璐 我一進(jìn)店門稻艰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人侈净,你說我怎么就攤上這事尊勿。” “怎么了畜侦?”我有些...
    開封第一講書人閱讀 162,632評論 0 353
  • 文/不壞的土叔 我叫張陵元扔,是天一觀的道長。 經(jīng)常有香客問我旋膳,道長澎语,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,180評論 1 292
  • 正文 為了忘掉前任验懊,我火速辦了婚禮擅羞,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘义图。我一直安慰自己减俏,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評論 6 388
  • 文/花漫 我一把揭開白布碱工。 她就那樣靜靜地躺著娃承,像睡著了一般。 火紅的嫁衣襯著肌膚如雪怕篷。 梳的紋絲不亂的頭發(fā)上历筝,一...
    開封第一講書人閱讀 51,165評論 1 299
  • 那天,我揣著相機(jī)與錄音匙头,去河邊找鬼漫谷。 笑死仔雷,一個胖子當(dāng)著我的面吹牛蹂析,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播碟婆,決...
    沈念sama閱讀 40,052評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼电抚,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了竖共?” 一聲冷哼從身側(cè)響起蝙叛,我...
    開封第一講書人閱讀 38,910評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎公给,沒想到半個月后借帘,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蜘渣,經(jīng)...
    沈念sama閱讀 45,324評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評論 2 332
  • 正文 我和宋清朗相戀三年肺然,在試婚紗的時候發(fā)現(xiàn)自己被綠了蔫缸。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,711評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡际起,死狀恐怖拾碌,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情街望,我是刑警寧澤校翔,帶...
    沈念sama閱讀 35,424評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站灾前,受9級特大地震影響防症,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜哎甲,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評論 3 326
  • 文/蒙蒙 一告希、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧烧给,春花似錦燕偶、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至榴鼎,卻和暖如春伯诬,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背巫财。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評論 1 269
  • 我被黑心中介騙來泰國打工盗似, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人平项。 一個月前我還...
    沈念sama閱讀 47,722評論 2 368
  • 正文 我出身青樓赫舒,卻偏偏與公主長得像,于是被迫代替她去往敵國和親闽瓢。 傳聞我的和親對象是個殘疾皇子接癌,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評論 2 353

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