Android 進程間通信AIDL(二)

本文主要來分析一下AIDL實現原理,在Android進程間通信AIDL(一)學習如何使用AIDL時,在Client端用到了IRemoteService這么一個類,廢話不多說,直接貼代碼

/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: D:\\Android\\demo\\AIDLDemo\\app\\src\\main\\aidl\\com\\zx\\aidl\\demo\\IRemoteService.aidl
 */
package com.zx.aidl.demo;

public interface IRemoteService extends android.os.IInterface {
    /** Local-side IPC implementation stub class. */
    public static abstract class Stub extends android.os.Binder
            implements com.zx.aidl.demo.IRemoteService {
        private static final java.lang.String DESCRIPTOR = "com.zx.aidl.demo.IRemoteService";

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

        /**
         * Cast an IBinder object into an com.zx.aidl.demo.IRemoteService interface,
         * generating a proxy if needed.
         */
        public static com.zx.aidl.demo.IRemoteService asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.zx.aidl.demo.IRemoteService))) {
                return ((com.zx.aidl.demo.IRemoteService) iin);
            }
            return new com.zx.aidl.demo.IRemoteService.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 {
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(DESCRIPTOR);
                    return true;
                }
                case TRANSACTION_addPerson: {
                    data.enforceInterface(DESCRIPTOR);
                    com.zx.aidl.demo.Person _arg0;
                    if ((0 != data.readInt())) {
                        _arg0 = com.zx.aidl.demo.Person.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    this.addPerson(_arg0);
                    reply.writeNoException();
                    return true;
                }
                case TRANSACTION_getPersons: {
                    data.enforceInterface(DESCRIPTOR);
                    java.util.List<com.zx.aidl.demo.Person> _result = this.getPersons();
                    reply.writeNoException();
                    reply.writeTypedList(_result);
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements com.zx.aidl.demo.IRemoteService {
            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 addPerson(com.zx.aidl.demo.Person person)
                    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 ((person != null)) {
                        _data.writeInt(1);
                        person.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                    mRemote.transact(Stub.TRANSACTION_addPerson, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }

            @Override public java.util.List<com.zx.aidl.demo.Person> getPersons()
                    throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.util.List<com.zx.aidl.demo.Person> _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_getPersons, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.createTypedArrayList(com.zx.aidl.demo.Person.CREATOR);
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }

        static final int TRANSACTION_addPerson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_getPersons = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    }

    public void addPerson(com.zx.aidl.demo.Person person) throws android.os.RemoteException;

    public java.util.List<com.zx.aidl.demo.Person> getPersons() throws android.os.RemoteException;
}

接下來我們一步一步分析上面代碼,首先看下它的注釋:

/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: D:\\Android\\demo\\AIDLDemo\\app\\src\\main\\aidl\\com\\zx\\aidl\\demo\\IRemoteService.aidl
 */

IRemoteService.java文件是SDK根據IRemoteService.aidl自動為我們生成的一個Java文件,并且不允許修改,那SDK為什么要為我們生成這個文件呢?首先我們知道ADIL進程間通信其實本質就是Binder機制,既然能讓客戶端訪問服務端,服務端就得公布其接口和方法供客戶端調用,IRemoteService正是充當了該角色,這里是SDK為了我們開發更簡單方便,自動生成了這么一個類,當然我們也可以自己手動去實現它。仔細查看這個接口,發現其結構其實很簡單,一個靜態內部抽象類和倆方法,我們來重點分析下內部抽象類Stub,主要來分析下它的幾個重要方法

  • asInterface(android.os.IBinder obj)
    這個方法其實是將Binder對象轉換成IRemoteService接口
 android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.zx.aidl.demo.IRemoteService))) {
                return ((com.zx.aidl.demo.IRemoteService) iin);
            }
            return new com.zx.aidl.demo.IRemoteService.Stub.Proxy(obj);

首先它會通過傳入的binder對象參數去查詢本地是否有該接口,其實也是判斷是否是本地通信,如果是,直接返回該接口,查詢的時候會傳入DESCRIPTOR這么一個參數,這個參數其實是Binder的唯一標識,一般都采用類名,如果不是本地通信,會返回Proxy這么一個對象,并且將傳入了Binder對象作為參數,因為我們主要分析的是進程間通信,所以分析第二種情況,我們去看下Proxy這個類到底是什么鬼?

   private static class Proxy implements com.zx.aidl.demo.IRemoteService {
            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 addPerson(com.zx.aidl.demo.Person person)
                    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 ((person != null)) {
                        _data.writeInt(1);
                        person.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                    mRemote.transact(Stub.TRANSACTION_addPerson, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }

            @Override public java.util.List<com.zx.aidl.demo.Person> getPersons()
                    throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.util.List<com.zx.aidl.demo.Person> _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_getPersons, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.createTypedArrayList(com.zx.aidl.demo.Person.CREATOR);
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }

        static final int TRANSACTION_addPerson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_getPersons = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    }

Proxy是Stub的一個靜態內部類,從字面意思就知道它其實是一個代理類,那它到底代理的是誰?顯然Proxy內部持有了一個 Ibinder 變量,所以它其實Binder的代理,其實也就是Stub的代理對象,Proxy也實現了IRemoteService接口以及addPerson和getPersons方法,我們這里選擇getPersons這個方法來分析,看它內部到底做了那些操作,為什么不用addPerson呢,沒什么原因,因為原理都一樣,就隨便拿一個方法來分析

       @Override public java.util.List<com.zx.aidl.demo.Person> getPersons()
                    throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.util.List<com.zx.aidl.demo.Person> _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_getPersons, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.createTypedArrayList(com.zx.aidl.demo.Person.CREATOR);
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

從 asInterface(android.os.IBinder obj) 這個方法我們知道,其實在Client端拿到的是Proxy這個對象,所以它的addPerson和getPersons方法其實是運行在Client端的,首先會創建倆個Parcel參數,輸入新參數_data和_reply,同時創建了一個List返回對象_result,接下來主要分為三個步驟:

  1. 將方法參數信息寫入_data中
  2. 調用transact方法像服務端發起RPC(Remote Procedure Cal)遠程調用請求,同時掛起客戶端線程,等待服務端的onTransact方法返回結果
  3. 從_reply中拿到結果并返回然后喚醒客戶端線程
    這里說下第二步,transact方法時在底層執行的,所以我們這里不用去深究方法里到底做了什么,只需要知道該方方法向服務端的發送了一個請求,然后服務端會執行它的onTransact方法,那么onTransact到底在什么地方,其實就是Stub類里面的onTransact方法,接下來我們去看下Stub類里面的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_addPerson: {
                    data.enforceInterface(DESCRIPTOR);
                    com.zx.aidl.demo.Person _arg0;
                    if ((0 != data.readInt())) {
                        _arg0 = com.zx.aidl.demo.Person.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    this.addPerson(_arg0);
                    reply.writeNoException();
                    return true;
                }
                case TRANSACTION_getPersons: {
                    data.enforceInterface(DESCRIPTOR);
                    java.util.List<com.zx.aidl.demo.Person> _result = this.getPersons();
                    reply.writeNoException();
                    reply.writeTypedList(_result);
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

首先會根據code去判斷客戶端請求目標方法,這里的code就是我們在上面transact方法中傳入的Stub.TRANSACTION_getPersons,所以我們直接去看相對應的方法處理

                    data.enforceInterface(DESCRIPTOR);
                    java.util.List<com.zx.aidl.demo.Person> _result = this.getPersons();
                    reply.writeNoException();
                    reply.writeTypedList(_result);
                    return true;

首先它會去調用自身的getPersons方法,其實就是我們在Service里實現的getPersons方法

    private IRemoteService.Stub binder = new IRemoteService.Stub() {
        @Override public void addPerson(Person person) throws RemoteException {
            persons.add(person);
        }

        @Override public List<Person> getPersons() throws RemoteException {
            return persons;
        }
    };

然后將結果寫入reply中返回給我們的客戶端,需要注意的是,最后方法返回值是true,如果返回false的話,客戶端會請求失敗,到這里我們整個流程分析就已經結束,接下來我們做一個總結,先上個圖

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

推薦閱讀更多精彩內容