AIDL的使用荚斯、傳遞復(fù)雜對象以及回調(diào)客戶端

前言

  • 此教程的目的是教會大家如何使用AIDL埠居,包括定義AIDL服務(wù)查牌、調(diào)用AIDL服務(wù)、傳遞復(fù)雜對象滥壕、AIDL回調(diào)客戶端等纸颜。

  • Github地址:https://github.com/daking1991/AIDLDemo

概述

  • 全稱Android Interface Definition Language。

  • 像其他IDLs一樣绎橘,允許你定義編程接口胁孙,以便客戶端和服務(wù)能通過內(nèi)部進(jìn)程通信(interprocess communication,IPC)。

定義AIDL服務(wù)

  1. 創(chuàng)建.aidl文件
  2. SDK生成對應(yīng).java文件和Stub內(nèi)部類
  3. 通過Service子類將接口暴露給外界

1. 創(chuàng)建.aidl文件

  • 用Java編程語言來構(gòu)造.aidl文件称鳞。每個.aidl文件必須定義一個帶方法聲明的接口涮较。

  • AIDL支持以下數(shù)據(jù)類型:

    1. Java基本類型,即int冈止、long狂票、char等;
    2. String熙暴;
    3. CharSequence闺属;
    4. List
      • List中的所有元素都必須是AIDL支持的數(shù)據(jù)類型、其他AIDL接口或你之前聲明的Parcelable實現(xiàn)類周霉。
    5. Map
      • Map中的所有元素都必須是AIDL支持的數(shù)據(jù)類型掂器、其他AIDL接口或你之前聲明的Parcelable實現(xiàn)類。
    6. 其他類型俱箱,必須要有import語句国瓮,即使它跟.aidl是同一個包下。
  • AIDL中的方法和變量

    • 方法可有零匠楚、一或多個參數(shù)巍膘,可有返回值或void。
    • 所有非基本類型的參數(shù)都需要標(biāo)簽來表明這個數(shù)據(jù)的去向:
      1. in芋簿,表示此變量由客戶端設(shè)置峡懈;
      2. out,表示此變量由服務(wù)端設(shè)置与斤;
      3. inout肪康,表示此變量可由客戶端和服務(wù)端設(shè)置;
      4. 基本類型只能是in撩穿。
    • 只expose方法磷支,不會expose靜態(tài)變量
  • .aidl文件保存在項目/src/<SourceSet>/aidl目錄下食寡。

    // IRemoteService.aidl
    package com.daking.aidl;
    
    // Declare any non-default types here with import statements
    
    interface IRemoteService {
     /**
        * Demonstrates some basic types that you can use as parameters
        * and return values in AIDL.
        */
        void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString);
    }
    

2. SDK生成對應(yīng).java文件和Stub內(nèi)部類

  • 當(dāng)編譯APP時雾狈,SDK工具會將項目/src/<SourceSet>/aidl目錄下的.aidl文件一個個在項目/build/generated/source/aidl目錄下生成IBinder接口.java文件。兩個文件名一樣抵皱,只是后綴不同善榛。如IRemoteService.aidl生成IRemoteService.java辩蛋。

  • Stub內(nèi)部類

    • .aidl文件編譯后生成的.java文件中自動生成的內(nèi)部類。
    • public static abstract聲明移盆。
    • extends android.os.Binder悼院。
    • 實現(xiàn).aidl文件中定義的接口,且聲明其所有方法咒循。
  • 實現(xiàn)Stub內(nèi)部類要注意

    • 對于傳過來的調(diào)用据途,無法保證是在主線程中執(zhí)行的。Service必須要考慮多線程和線程安全叙甸。
    • 默認(rèn)情況下颖医,RPC都是異步的。避免在主線程中調(diào)用AIDL蚁署,不然可能會導(dǎo)致ANR便脊。
    • 不能給調(diào)用方回拋異常

3. 通過Service子類將接口暴露給外界

  • 需要創(chuàng)建Service子類光戈,并在onBind()中返回Stub內(nèi)部類。

    package com.daking.aidl;
    
    import android.app.Service;
    import android.content.Intent;
    import android.os.IBinder;
    import android.os.RemoteException;
    
    public class RemoteService extends Service {
        public RemoteService() {}
    
        @Override 
        public IBinder onBind(Intent intent) {
            return mBinder;
        }
    
        private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {
            @Override
            public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
            
            }
        };
    }
    
  • <service>配置遂赠,設(shè)置exported為true久妆、自定義action名等。

    <service
        android:name=".RemoteService"
        android:enabled="true"
        android:exported="true">
        <intent-filter>
            <action android:name="com.daking.aidl.RemoteService" />
            <category android:name="android.intent.category.DEFAULT" />
        </intent-filter>
    </service>
    

調(diào)用AIDL服務(wù)

  • 若客戶端組件和服務(wù)分開在不同APP跷睦,那么客戶端所在APP的/src/<SourceSet>/aidl目錄下必須要有一份.aidl副本筷弦。

  • 綁定AIDL服務(wù)

    Intent intent = new Intent();
    intent.setPackage("com.daking.aidl");
    intent.setAction("com.daking.aidl.RemoteService");
    bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
    
  • 當(dāng)客戶端onServiceConnected()接收到這個AIDL服務(wù)返回的IBinder時,必須要將其強(qiáng)制類型轉(zhuǎn)換為YourServiceInterface類型抑诸。如

    public void onServiceConnected(ComponentName className, IBinder service) {
        mIRemoteService = IRemoteService.Stub.asInterface(service);
    } 
    
  • 可像普通對象一樣調(diào)用mIRemoteService烂琴。注意,AIDL服務(wù)默認(rèn)是運(yùn)行在主線程中蜕乡,若里面有耗時操作奸绷,應(yīng)該在子線程中調(diào)用AIDL。

傳遞復(fù)雜對象

  • 在AIDL中傳遞的復(fù)雜對象必須要實現(xiàn)Parcelable接口层玲,這是因為Parcelable允許Android系統(tǒng)將復(fù)雜對象分解成基本類型以便在進(jìn)程間傳輸号醉。

  • Parcelable實現(xiàn)類

    1. implements Parcelable;
    2. 實現(xiàn)writeToParcel()辛块,它會讀取這個對象的當(dāng)前狀態(tài)并寫入一個包中畔派;
    3. 實現(xiàn)describeContents();
    4. 添加一個實現(xiàn)Parcelable.Creator接口的靜態(tài)變量CREATOR润绵。
    package com.daking.aidl;
    
    import android.os.Parcel;
    import android.os.Parcelable;
    
    public class RequestVO implements Parcelable {
    
        private String name;
        private String type;
        
        public RequestVO() {}
        
        public RequestVO(Parcel source) {
            super();
            this.setName(source.readString());
            this.setType(source.readString());
        }
        
        public String getName() {
            return name;
        }
        
        public void setName(String name) {
            this.name = name;
        }
        
        public String getType() {
            return type;
        }
        
        public void setType(String type) {
            this.type = type;
        }
        
        public int describeContents() {
            return 0;
        }
        
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeString(name);
            dest.writeString(type);
        }
        
        public static final Parcelable.Creator<RequestVO> CREATOR = new Parcelable.Creator<RequestVO>() {
            public RequestVO createFromParcel(Parcel source) {
                return new RequestVO(source);
            }
            
            public RequestVO[] newArray(int size) {
                return new RequestVO[size];
            }
        };
    }
    
  • 若客戶端組件和服務(wù)分開在不同APP线椰,必須要把該P(yáng)arcelable實現(xiàn)類.java文件拷貝到客戶端所在的APP,包路徑要一致尘盼。

  • 另外憨愉,需要為這個Parcelable實現(xiàn)類定義一個相應(yīng)的.aidl文件烦绳。與AIDL服務(wù)接口.aidl同理,客戶端所在APP的/src/<SourceSet>/aidl目錄下也要有這份副本莱衩。

    package com.daking.aidl;
    
    parcelable RequestVO;
    
  • 將復(fù)雜對象作為AIDL接口的形參時爵嗅,記得加上in

    import com.daking.aidl.RequestVO;
    
    interface IRemoteService {
        void request(in RequestVO vo);
    }
    

AIDL服務(wù)回調(diào)客戶端

  1. 自定義回調(diào)接口.aidl笨蚁。

    package com.daking.aidl;
    
    import com.daking.aidl.ResponseVO; // 自定義結(jié)構(gòu)類睹晒,具體實現(xiàn)可參考上一節(jié)。
    
    interface ICallback {
        void onResult(in ResponseVO vo);
    }
    
  2. AIDL服務(wù).aidl提供接口給客戶端注冊和注銷此回調(diào)括细。

    package com.daking.aidl;
    
    import com.daking.aidl.RequestVO;
    import com.daking.aidl.ICallback;
    
    interface IRemoteService {
        void request(in RequestVO vo);
        void registerCallback(in ICallback cb);
        void unregisterCallback(in ICallback cb);
    }
    
  3. AIDL服務(wù).java的具體實現(xiàn)伪很。

    public class RemoteService extends Service {
        // ICallback列表
        private RemoteCallbackList<ICallback> icallbacks;
    
        @Override
        public IBinder onBind(Intent intent) {
            icallbacks = new RemoteCallback<ICallback>();
            return mBinder;
        }
        
        private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {
            @Override
            public void request(in RequestVO vo) {
                sendResponse();
            }
            @Override
            public void registerCallback(in ICallback cb) {
                if(cb != null) {
                    icallbacks.register(cb);
                }
            }
            @Override
            public void unregisterCallback(in ICallback cb) {
                if(cb != null) {
                    icallbacks.unregister(cb);
                }
            }
        };
        
        private void sendResponse() {
            ResponseVO vo = new ResponseVO();
            
            // 以廣播的方式進(jìn)行客戶端回調(diào)
            int len = icallbacks.beginBroadcast();
            for (int i = 0; i < len; i++) {
                try {
                    icallbacks.getBroadcastItem(i).onResult(vo);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
            // 記得要關(guān)閉廣播
            icallbacks.finishBroadcast();
        }
    }
    
  4. 客戶端創(chuàng)建回調(diào)接口的實現(xiàn)對象,并注冊到AIDL奋单。

    protected ICallback callback = new ICallback.Stub() {
        @Override
        public void onResult(ResponseVO vo) {
            // AIDL回調(diào)客戶端后的業(yè)務(wù)處理
        }
    };
    
    // mService為AIDL服務(wù)
    mService.registerCallback(callback);
    

我的博客

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末锉试,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子览濒,更是在濱河造成了極大的恐慌呆盖,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,695評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件贷笛,死亡現(xiàn)場離奇詭異应又,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)乏苦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評論 3 399
  • 文/潘曉璐 我一進(jìn)店門株扛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人汇荐,你說我怎么就攤上這事洞就。” “怎么了掀淘?”我有些...
    開封第一講書人閱讀 168,130評論 0 360
  • 文/不壞的土叔 我叫張陵旬蟋,是天一觀的道長。 經(jīng)常有香客問我繁疤,道長咖为,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,648評論 1 297
  • 正文 為了忘掉前任稠腊,我火速辦了婚禮躁染,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘架忌。我一直安慰自己吞彤,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,655評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著饰恕,像睡著了一般挠羔。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上埋嵌,一...
    開封第一講書人閱讀 52,268評論 1 309
  • 那天破加,我揣著相機(jī)與錄音,去河邊找鬼雹嗦。 笑死范舀,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的了罪。 我是一名探鬼主播锭环,決...
    沈念sama閱讀 40,835評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼泊藕!你這毒婦竟也來了辅辩?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,740評論 0 276
  • 序言:老撾萬榮一對情侶失蹤娃圆,失蹤者是張志新(化名)和其女友劉穎玫锋,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體讼呢,經(jīng)...
    沈念sama閱讀 46,286評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡景醇,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,375評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了吝岭。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,505評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡吧寺,死狀恐怖窜管,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情稚机,我是刑警寧澤幕帆,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站赖条,受9級特大地震影響失乾,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜纬乍,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,873評論 3 333
  • 文/蒙蒙 一碱茁、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧仿贬,春花似錦纽竣、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,357評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽聋袋。三九已至,卻和暖如春穴吹,著一層夾襖步出監(jiān)牢的瞬間幽勒,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,466評論 1 272
  • 我被黑心中介騙來泰國打工港令, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留啥容,地道東北人。 一個月前我還...
    沈念sama閱讀 48,921評論 3 376
  • 正文 我出身青樓缠借,卻偏偏與公主長得像干毅,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子泼返,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,515評論 2 359

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

  • Jianwei's blog 首頁 分類 關(guān)于 歸檔 標(biāo)簽 巧用Android多進(jìn)程硝逢,微信,微博等主流App都在用...
    justCode_閱讀 5,925評論 1 23
  • Android中AIDL的基本用法Android 中AIDL的使用與理解Android AIDL使用詳解徹底明白A...
    TTTqiu閱讀 1,145評論 0 1
  • 上篇文章介紹了IPC機(jī)制的基本概念以及簡單使用绅喉,文章鏈接:Android 關(guān)于IPC機(jī)制的理解(一) 這篇文章主要...
    老實任閱讀 729評論 0 2
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理渠鸽,服務(wù)發(fā)現(xiàn),斷路器柴罐,智...
    卡卡羅2017閱讀 134,701評論 18 139
  • 你說 我睡覺是因為下午要聽課 我答 我睡覺是因為昨晚睡得少 其實 睡覺是因為 我不開心 心理老師說 不開心你就睡覺...
    可愛蛋黃派閱讀 523評論 0 1