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

前言

  • 此教程的目的是教會(huì)大家如何使用AIDL,包括定義AIDL服務(wù)、調(diào)用AIDL服務(wù)、傳遞復(fù)雜對(duì)象、AIDL回調(diào)客戶(hù)端等。

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

概述

  • 全稱(chēng)Android Interface Definition Language。

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

定義AIDL服務(wù)

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

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

  • 用Java編程語(yǔ)言來(lái)構(gòu)造.aidl文件。每個(gè).aidl文件必須定義一個(gè)帶方法聲明的接口。

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

    1. Java基本類(lèi)型,即int、long、char等;
    2. String;
    3. CharSequence;
    4. List
      • List中的所有元素都必須是AIDL支持的數(shù)據(jù)類(lèi)型、其他AIDL接口或你之前聲明的Parcelable實(shí)現(xiàn)類(lèi)。
    5. Map
      • Map中的所有元素都必須是AIDL支持的數(shù)據(jù)類(lèi)型、其他AIDL接口或你之前聲明的Parcelable實(shí)現(xiàn)類(lèi)。
    6. 其他類(lèi)型,必須要有import語(yǔ)句,即使它跟.aidl是同一個(gè)包下。
  • AIDL中的方法和變量

    • 方法可有零、一或多個(gè)參數(shù),可有返回值或void。
    • 所有非基本類(lèi)型的參數(shù)都需要標(biāo)簽來(lái)表明這個(gè)數(shù)據(jù)的去向:
      1. in,表示此變量由客戶(hù)端設(shè)置;
      2. out,表示此變量由服務(wù)端設(shè)置;
      3. inout,表示此變量可由客戶(hù)端和服務(wù)端設(shè)置;
      4. 基本類(lèi)型只能是in。
    • 只expose方法,不會(huì)expose靜態(tài)變量。
  • .aidl文件保存在項(xiàng)目/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生成對(duì)應(yīng).java文件和Stub內(nèi)部類(lèi)

  • 當(dāng)編譯APP時(shí),SDK工具會(huì)將項(xiàng)目/src/<SourceSet>/aidl目錄下的.aidl文件一個(gè)個(gè)在項(xiàng)目/build/generated/source/aidl目錄下生成IBinder接口.java文件。兩個(gè)文件名一樣,只是后綴不同。如IRemoteService.aidl生成IRemoteService.java。

  • Stub內(nèi)部類(lèi)

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

    • 對(duì)于傳過(guò)來(lái)的調(diào)用,無(wú)法保證是在主線(xiàn)程中執(zhí)行的。Service必須要考慮多線(xiàn)程和線(xiàn)程安全。
    • 默認(rèn)情況下,RPC都是異步的。避免在主線(xiàn)程中調(diào)用AIDL,不然可能會(huì)導(dǎo)致ANR。
    • 不能給調(diào)用方回拋異常。

3. 通過(guò)Service子類(lèi)將接口暴露給外界

  • 需要?jiǎng)?chuàng)建Service子類(lèi),并在onBind()中返回Stub內(nèi)部類(lè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ù)

  • 若客戶(hù)端組件和服務(wù)分開(kāi)在不同APP,那么客戶(hù)端所在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)客戶(hù)端onServiceConnected()接收到這個(gè)AIDL服務(wù)返回的IBinder時(shí),必須要將其強(qiáng)制類(lèi)型轉(zhuǎn)換為YourServiceInterface類(lèi)型。如

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

傳遞復(fù)雜對(duì)象

  • 在AIDL中傳遞的復(fù)雜對(duì)象必須要實(shí)現(xiàn)Parcelable接口,這是因?yàn)镻arcelable允許Android系統(tǒng)將復(fù)雜對(duì)象分解成基本類(lèi)型以便在進(jìn)程間傳輸。

  • Parcelable實(shí)現(xiàn)類(lèi)

    1. implements Parcelable;
    2. 實(shí)現(xiàn)writeToParcel(),它會(huì)讀取這個(gè)對(duì)象的當(dāng)前狀態(tài)并寫(xiě)入一個(gè)包中;
    3. 實(shí)現(xiàn)describeContents();
    4. 添加一個(gè)實(shí)現(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];
            }
        };
    }
    
  • 若客戶(hù)端組件和服務(wù)分開(kāi)在不同APP,必須要把該P(yáng)arcelable實(shí)現(xiàn)類(lèi).java文件拷貝到客戶(hù)端所在的APP,包路徑要一致。

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

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

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

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

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

    package com.daking.aidl;
    
    import com.daking.aidl.ResponseVO; // 自定義結(jié)構(gòu)類(lèi),具體實(shí)現(xiàn)可參考上一節(jié)。
    
    interface ICallback {
        void onResult(in ResponseVO vo);
    }
    
  2. AIDL服務(wù).aidl提供接口給客戶(hù)端注冊(cè)和注銷(xiāo)此回調(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的具體實(shí)現(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)行客戶(hù)端回調(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. 客戶(hù)端創(chuàng)建回調(diào)接口的實(shí)現(xiàn)對(duì)象,并注冊(cè)到AIDL。

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

我的博客

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Jianwei's blog 首頁(yè) 分類(lèi) 關(guān)于 歸檔 標(biāo)簽 巧用Android多進(jìn)程,微信,微博等主流App都在用...
    justCode_閱讀 6,133評(píng)論 1 23
  • Android中AIDL的基本用法Android 中AIDL的使用與理解Android AIDL使用詳解徹底明白A...
    TTTqiu閱讀 1,247評(píng)論 0 1
  • 上篇文章介紹了IPC機(jī)制的基本概念以及簡(jiǎn)單使用,文章鏈接:Android 關(guān)于IPC機(jī)制的理解(一) 這篇文章主要...
    老實(shí)任閱讀 898評(píng)論 0 2
  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,699評(píng)論 19 139
  • 你說(shuō) 我睡覺(jué)是因?yàn)橄挛缫?tīng)課 我答 我睡覺(jué)是因?yàn)樽蛲硭蒙?其實(shí) 睡覺(jué)是因?yàn)?我不開(kāi)心 心理老師說(shuō) 不開(kāi)心你就睡覺(jué)...
    可愛(ài)蛋黃派閱讀 553評(píng)論 0 1

友情鏈接更多精彩內(nèi)容