前言
此教程的目的是教會大家如何使用AIDL埠居,包括定義AIDL服務(wù)查牌、調(diào)用AIDL服務(wù)、傳遞復(fù)雜對象滥壕、AIDL回調(diào)客戶端等纸颜。
概述
全稱Android Interface Definition Language。
像其他IDLs一樣绎橘,允許你定義編程接口胁孙,以便客戶端和服務(wù)能通過內(nèi)部進(jìn)程通信(interprocess communication,IPC)。
定義AIDL服務(wù)
- 創(chuàng)建.aidl文件
- SDK生成對應(yīng).java文件和Stub內(nèi)部類
- 通過Service子類將接口暴露給外界
1. 創(chuàng)建.aidl文件
用Java編程語言來構(gòu)造.aidl文件称鳞。每個.aidl文件必須定義一個帶方法聲明的接口涮较。
-
AIDL支持以下數(shù)據(jù)類型:
- Java基本類型,即int冈止、long狂票、char等;
- String熙暴;
- CharSequence闺属;
- List
- List中的所有元素都必須是AIDL支持的數(shù)據(jù)類型、其他AIDL接口或你之前聲明的Parcelable實現(xiàn)類周霉。
- Map
- Map中的所有元素都必須是AIDL支持的數(shù)據(jù)類型掂器、其他AIDL接口或你之前聲明的Parcelable實現(xiàn)類。
- 其他類型俱箱,必須要有import語句国瓮,即使它跟.aidl是同一個包下。
-
AIDL中的方法和變量
- 方法可有零匠楚、一或多個參數(shù)巍膘,可有返回值或void。
- 所有非基本類型的參數(shù)都需要標(biāo)簽來表明這個數(shù)據(jù)的去向:
- in芋簿,表示此變量由客戶端設(shè)置峡懈;
- out,表示此變量由服務(wù)端設(shè)置与斤;
- inout肪康,表示此變量可由客戶端和服務(wù)端設(shè)置;
- 基本類型只能是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)類
- implements Parcelable;
- 實現(xiàn)writeToParcel()辛块,它會讀取這個對象的當(dāng)前狀態(tài)并寫入一個包中畔派;
- 實現(xiàn)describeContents();
- 添加一個實現(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)客戶端
-
自定義回調(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); }
-
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); }
-
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(); } }
-
客戶端創(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);