Android AIDL詳解

AIDL簡介


AIDL是對以 Binder 為核心的跨進程通信機制的進一步封裝某抓,使得這個過程更加簡化惰瓜,只暴露出非常必要的開發(fā)者需要實現(xiàn)的接口崎坊,讓開發(fā)人員實現(xiàn)業(yè)務邏輯。顧名思義曲尸,Android 接口定義語言另患。
既然還是以針對跨進程訪問蛾绎,不過就是客戶端進程和服務端服務進程秘通,且最好的使用場景是客戶端進程和服務端進程不在同一個進程中。而訪問和通信基礎實際上來自于 Binder 機制第股。下面夕吻,首先我們針對上面這些語言中出現(xiàn)的關鍵詞進行解析繁仁,即客戶端進程黄虱,服務進程捻浦,Binder。

客戶端進程

客戶端昧识,我們都知道這是一個相對于服務端而言的概念盗扒。
客戶端通常并不處理具體邏輯,而只是請求向服務端請求結(jié)果朵耕,然后對結(jié)果進行處理淋叶。即煞檩,向服務端發(fā)出request斟湃,然后有服務端進行業(yè)務邏輯處理,然后返回給客戶端response注暗,客戶端拿到response捆昏,做進一步處理(比如UI展示等)毙沾。
在Android應用開發(fā)過程中骗卜,客戶端可以是任何組件,我們常用的activity左胞,service(相對于另外一個service而言寇仓,它就是一個客戶端)等。

服務端進程

說到服務端烤宙,下意識會想到服務器遍烦。服務端的優(yōu)點在于,擁有強大的業(yè)務處理能力躺枕,實現(xiàn)了業(yè)務處理的集中管理服猪,同時還可能增加更多的安全屏障屯远。
在Android應用開發(fā)中蔓姚,各種**ServiceManager都是服務端進程,或者我們實現(xiàn)的Service慨丐,也可以作為服務端進程。
說到Service泄私,提一點題外話房揭,來自于官網(wǎng)對Service的說明:

Service(服務)基本上分為兩種形式:
啟動
當應用組件(如 Activity)通過調(diào)用 startService()
啟動服務時备闲,服務即處于“啟動”狀態(tài)。一旦啟動捅暴,服務即可在后臺無限期運行恬砂,即使啟動服務的組件已被銷毀也不受影響。 已啟動的服務通常是執(zhí)行單一操作蓬痒,而且不會將結(jié)果返回給調(diào)用方泻骤。例如,它可能通過網(wǎng)絡下載或上傳文件梧奢。 操作完成后狱掂,服務會自行停止運行。
綁定
當應用組件通過調(diào)用 bindService()
綁定到服務時亲轨,服務即處于“綁定”狀態(tài)趋惨。綁定服務提供了一個客戶端-服務器接口,允許組件與服務進行交互惦蚊、發(fā)送請求器虾、獲取結(jié)果,甚至是利用進程間通信 (IPC) 跨進程執(zhí)行這些操作蹦锋。 僅當與另一個應用組件綁定時兆沙,綁定服務才會運行。 多個組件可以同時綁定到該服務莉掂,但全部取消綁定后挤悉,該服務即會被銷毀。

通過上面巫湘,我們應該可以看到 bindService 的優(yōu)勢和使用場景装悲,這也就是為什么有時候我們需要定義客戶端,定義服務端尚氛,主要還是便于交互啊诀诊。當然,你說我不使用這種方式阅嘶,就不能實現(xiàn)和服務的交互了嗎属瓣?當然不是啊,實現(xiàn)方式肯定不止一種讯柔,但是這種方式無疑是最方便的吧抡蛙。

IBinder

根據(jù)官方文檔[譯]:

接口IBinder 是輕量級高性能遠程調(diào)用機制的核心部分。
該接口描述了和遠程對象交互的抽象協(xié)議魂迄,但是通常我們都不會直接實現(xiàn)它粗截,而是繼承自 Binder。
IBinder 接口中關鍵 API 就是 transact()捣炬,與之相對的是 Binder.onTransact() 方法熊昌。其中绽榛,transact 方法將允許你發(fā)送一個調(diào)用到 IBinder 對象;Binder.onTransact()方法用以接收并處理調(diào)用婿屹。transact方法是一個同步方法灭美,它會一直等到onTransact處理完成返回之后,才會返回昂利。
通過 transact() 發(fā)送的數(shù)據(jù)是 Parcel届腐,本質(zhì)上,Parcel是一般的緩沖區(qū)蜂奸,持有內(nèi)容以及內(nèi)容元數(shù)據(jù)犁苏,而這些元數(shù)據(jù)被用來管理緩沖區(qū)中 IBinder 對象的引用。
這種機制確保了窝撵,當一個IBinder 被寫入到Parcel并發(fā)送到另外一個進程中傀顾,然后又從另外一個進程中發(fā)回來時,原來的進程能收到同樣的IBinder對象碌奉。這種語法短曾,還允許IBinder、Binder對象使用一個唯一的id(可以作為token或者其他目的)以便進行跨進程管理赐劣。
每個運行的進程中嫉拐,都有一個用于 transaction 的線程池。這些線程池被用來處理所有的來自于其它進程的IPC魁兼。舉個例子婉徘,從進程A向進程B進行IPC,A中的線程阻塞在 transact()當它將事務發(fā)送到進程B 時咐汞。
B中的線程池接收到該事務盖呼,在目標對象上調(diào)用Binder.onTransact(),接著回復一個Parcel作為結(jié)果化撕。收到結(jié)果之后几晤,進程A繼續(xù)執(zhí)行。在效果上植阴,就像是在同一個進程中創(chuàng)建一個線程執(zhí)行蟹瘾。

Binder

根據(jù)官方文檔[譯]:

實現(xiàn)了IBinder接口,是輕量級跨進程訪問機制的核心掠手。對于很多開發(fā)者而言憾朴,不需要直接使用這個類,而是描述好希望的接口喷鸽,然后由 aidl 工具生成合適的 Binder 子類众雷。

關于Binder的進一步解釋,可以參考我的另外一篇文章圖說Android Binder機制

AIDL使用


關于aidl在 android studio 中操作方式,網(wǎng)上教程有很多很多报腔,這里就不再描述具體該怎么在項目中使用了株搔,而是直接上代碼剖淀,代碼中有注釋纯蛾,描述了關鍵步驟。

public interface IRemoteService extends android.os.IInterface {
    /**
     * Local-side IPC implementation stub class.
     */
    //在服務端將實現(xiàn)該類纵隔,并在onTransact中處理客戶端發(fā)送過來的實體
    public static abstract class Stub extends android.os.Binder implements com.ugym.server.IRemoteService {
        private static final java.lang.String DESCRIPTOR = "com.test.server.IRemoteService";

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

        /**
         * Cast an IBinder object into an com.ugym.server.IRemoteService interface,
         * generating a proxy if needed.
         */
        public static com.test.server.IRemoteService asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.test.server.IRemoteService))) {
                return ((com.test.server.IRemoteService) iin);
            }
            //在客戶端將使用的是代理類翻诉,即這個Proxy
            return new com.test.server.IRemoteService.Stub.Proxy(obj);
        }

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

        // 服務端會在 onTransact 中處理來自客戶端的Parcel
        @Override
        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
            //根據(jù) code 判斷,客戶端是想調(diào)用哪個方法
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(DESCRIPTOR);
                    return true;
                }
                case TRANSACTION_basicTypes: {
                    data.enforceInterface(DESCRIPTOR);
                    int _arg0;
                    //獲取調(diào)用參數(shù)
                    _arg0 = data.readInt();
                    long _arg1;
                    //獲取調(diào)用參數(shù)
                    _arg1 = data.readLong();
                    boolean _arg2;
                    _arg2 = (0 != data.readInt());
                    float _arg3;
                    _arg3 = data.readFloat();
                    double _arg4;
                    _arg4 = data.readDouble();
                    java.lang.String _arg5;
                    _arg5 = data.readString();
                    this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
                    reply.writeNoException();
                    return true;
                }
                case TRANSACTION_addUser: {
                    data.enforceInterface(DESCRIPTOR);
                    com.test.server.User _arg0;
                    if ((0 != data.readInt())) {
                       //獲取調(diào)用參數(shù)
                        _arg0 = com.test.server.User.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    //該addUser方法就是在服務端自己實現(xiàn)的
                    this.addUser(_arg0);
                    reply.writeNoException();
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements com.test.server.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;
            }

            /**
             * Demonstrates some basic types that you can use as parameters
             * and return values in AIDL.
             */
            @Override
            public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) 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);
                    _data.writeLong(aLong);
                    _data.writeInt(((aBoolean) ? (1) : (0)));
                    _data.writeFloat(aFloat);
                    _data.writeDouble(aDouble);
                    _data.writeString(aString);
                    mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }

            @Override
            public void addUser(com.test.server.User user) throws android.os.RemoteException {
                //獲取Parcel對象捌刮,調(diào)用參數(shù)
                android.os.Parcel _data = android.os.Parcel.obtain();
                //獲取Parcel對象碰煌,返回結(jié)果
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    if ((user != null)) {
                        _data.writeInt(1);
                        user.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                    //客戶端通過 transact 進行遠程調(diào)用
                    mRemote.transact(Stub.TRANSACTION_addUser, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
        }
        //為每個函數(shù)進行編號
        static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        //addUser 函數(shù)的編號
        static final int TRANSACTION_addUser = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    }

    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;
// in 表示傳入數(shù)據(jù), out 表示傳出數(shù)據(jù)绅作, inout 表示雙向傳遞芦圾。注意含有 out 時 User 類需要實現(xiàn) readFromParcel() 方法

    public void addUser(com.test.server.User user) throws android.os.RemoteException;
}

在服務端做代碼實現(xiàn),進行業(yè)務邏輯處理:

public class RemoteService extends Service {


    private IBinder mIBinder = new IRemoteService.Stub() {
        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {

        }

        @Override
        public void addUser(User user) throws RemoteException {
            Log.d("RemoteService","addUser") ;
        }
    };

    public RemoteService() {
    }

    @Override
    public IBinder onBind(Intent intent) {

        try {
            mIBinder.linkToDeath(new IBinder.DeathRecipient() {
                @Override
                public void binderDied() {

                }
            },0);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
        return mIBinder ;
    }
}

當進行bindService后俄认,所有映射關系將被建立好个少。最終,客戶端將得到服務端Binder的句柄眯杏,以此夜焦,通過Binder驅(qū)動和服務端通信。

寫在最后


最后岂贩,還是那句話茫经,AIDL 說白了,從上層看萎津,就是一個通信協(xié)議卸伞。協(xié)議,就是規(guī)矩锉屈,就是約定荤傲,就是結(jié)構,就是架構部念。亦如 Http弃酌, 亦如 TCP,亦如IP儡炼。具體體現(xiàn)形式并不重要妓湘,重要的是 思路, 是結(jié)構化乌询。

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末榜贴,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌唬党,老刑警劉巖鹃共,帶你破解...
    沈念sama閱讀 211,042評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異驶拱,居然都是意外死亡霜浴,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評論 2 384
  • 文/潘曉璐 我一進店門蓝纲,熙熙樓的掌柜王于貴愁眉苦臉地迎上來阴孟,“玉大人,你說我怎么就攤上這事税迷∮浪浚” “怎么了?”我有些...
    開封第一講書人閱讀 156,674評論 0 345
  • 文/不壞的土叔 我叫張陵箭养,是天一觀的道長慕嚷。 經(jīng)常有香客問我,道長毕泌,這世上最難降的妖魔是什么喝检? 我笑而不...
    開封第一講書人閱讀 56,340評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮懈词,結(jié)果婚禮上蛇耀,老公的妹妹穿的比我還像新娘。我一直安慰自己坎弯,他們只是感情好纺涤,可當我...
    茶點故事閱讀 65,404評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著抠忘,像睡著了一般撩炊。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上崎脉,一...
    開封第一講書人閱讀 49,749評論 1 289
  • 那天拧咳,我揣著相機與錄音,去河邊找鬼囚灼。 笑死骆膝,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的灶体。 我是一名探鬼主播阅签,決...
    沈念sama閱讀 38,902評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼蝎抽!你這毒婦竟也來了政钟?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,662評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎养交,沒想到半個月后精算,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,110評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡碎连,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年灰羽,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片破花。...
    茶點故事閱讀 38,577評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡谦趣,死狀恐怖疲吸,靈堂內(nèi)的尸體忽然破棺而出座每,到底是詐尸還是另有隱情,我是刑警寧澤摘悴,帶...
    沈念sama閱讀 34,258評論 4 328
  • 正文 年R本政府宣布峭梳,位于F島的核電站,受9級特大地震影響蹂喻,放射性物質(zhì)發(fā)生泄漏葱椭。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,848評論 3 312
  • 文/蒙蒙 一口四、第九天 我趴在偏房一處隱蔽的房頂上張望孵运。 院中可真熱鬧,春花似錦蔓彩、人聲如沸治笨。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽旷赖。三九已至,卻和暖如春更卒,著一層夾襖步出監(jiān)牢的瞬間等孵,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評論 1 264
  • 我被黑心中介騙來泰國打工蹂空, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留俯萌,地道東北人。 一個月前我還...
    沈念sama閱讀 46,271評論 2 360
  • 正文 我出身青樓上枕,卻偏偏與公主長得像咐熙,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子姿骏,可洞房花燭夜當晚...
    茶點故事閱讀 43,452評論 2 348

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