前言
此教程的目的是教會(huì)大家如何使用AIDL,包括定義AIDL服務(wù)、調(diào)用AIDL服務(wù)、傳遞復(fù)雜對(duì)象、AIDL回調(diào)客戶(hù)端等。
概述
全稱(chēng)Android Interface Definition Language。
像其他IDLs一樣,允許你定義編程接口,以便客戶(hù)端和服務(wù)能通過(guò)內(nèi)部進(jìn)程通信(interprocess communication,IPC)。
定義AIDL服務(wù)
- 創(chuàng)建.aidl文件
- SDK生成對(duì)應(yīng).java文件和Stub內(nèi)部類(lèi)
- 通過(guò)Service子類(lèi)將接口暴露給外界
1. 創(chuàng)建.aidl文件
用Java編程語(yǔ)言來(lái)構(gòu)造.aidl文件。每個(gè).aidl文件必須定義一個(gè)帶方法聲明的接口。
-
AIDL支持以下數(shù)據(jù)類(lèi)型:
- Java基本類(lèi)型,即int、long、char等;
- String;
- CharSequence;
- List
- List中的所有元素都必須是AIDL支持的數(shù)據(jù)類(lèi)型、其他AIDL接口或你之前聲明的Parcelable實(shí)現(xiàn)類(lèi)。
- Map
- Map中的所有元素都必須是AIDL支持的數(shù)據(jù)類(lèi)型、其他AIDL接口或你之前聲明的Parcelable實(shí)現(xiàn)類(lèi)。
- 其他類(lèi)型,必須要有import語(yǔ)句,即使它跟.aidl是同一個(gè)包下。
-
AIDL中的方法和變量
- 方法可有零、一或多個(gè)參數(shù),可有返回值或void。
- 所有非基本類(lèi)型的參數(shù)都需要標(biāo)簽來(lái)表明這個(gè)數(shù)據(jù)的去向:
- in,表示此變量由客戶(hù)端設(shè)置;
- out,表示此變量由服務(wù)端設(shè)置;
- inout,表示此變量可由客戶(hù)端和服務(wù)端設(shè)置;
- 基本類(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)
- implements Parcelable;
- 實(shí)現(xiàn)writeToParcel(),它會(huì)讀取這個(gè)對(duì)象的當(dāng)前狀態(tài)并寫(xiě)入一個(gè)包中;
- 實(shí)現(xiàn)describeContents();
- 添加一個(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ù)端
-
自定義回調(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); } -
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); } -
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(); } } -
客戶(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);