AIDL使用介紹
AIDL的全稱是Android Interface definition language,一看就明白,它是一種android內部進程通信接口的描述語言,通過它我們可以定義進程間的通信接口,用處當然就是用來進程間的通信和方法調用了。其實AIDL是Binder的一個上層實現,它簡化了Binder的使用,在編譯時,由編譯器幫助我們完成了大量工作(例如,生成AIDL文件所對應的.java類)。
先介紹一下AIDL進程間通信的流程。
AIDL通信流程簡介
1. AIDL接口的創建
AIDL文件中,並不是所有的數據類型都是可以使用的,它支持的數據類型有:
- 基本數據類型(int,long,char,boolean,double等)
- String和CharSequence
- List:只支持ArrayList,而且list中的元素也必須是 AIDL 支持的類型
- Map:只支持HashMap,裏面的key和value也必須是AIDL支持的類型
- Parceable:所有實現了 Parceable 接口的對象
- AIDL:所有的 AIDL 接口本身也可以在 AIDL 文件中使用,所以 IBinder 類型也是支持的
2. 服務端
服務端首先要創建一個 Service 用來監聽客戶端的請求,然後將在對應AIDL文件中聲明的接口實現,並且通過onbind函數返回相應 IBinder 對象即可。
3. 客戶端
客戶端所要做的事情就稍微簡單一些,首先需要綁定服務端的Service,綁定成功後,將服務端返回的 IBinder 對象轉成AIDL接口所屬的類型,接着就可以調用AIDL中的方法了。
AIDL文件及參數說明
自定義類型
通常,AIDL默認支持基礎類型參數及返回值的數據傳遞。如果AIDL文件中用到了自定義的Parcelable對象,那麼必須新建一個和它同名的AIDL文件,並在其中聲明它爲Parcelable類型。
例如,我們定義了一個名爲RequestData的數據類型,要想在AIDL中使用它,除了它自己的RequestData.java文件之外,我們還必須定義一個名爲RequestData.aidl的文件(包名必須和RequestData.java中的相同),內容如下:
package com.testaidl;
parcelable RequestData;
參數標誌
AIDL中除了基本數據類型,其他類型的參數必須標上方向:in,out或者inout, in表示輸入型參數,out表示輸出型參數,inout表示輸入輸出型參數。
我們要根據實際需要去指定參數類型,不能一概使用out或者inout,因爲這在底層實現是有開銷的。
接口及同步
AIDL接口中只支持方法,不支持聲明靜態常量,這一點區別於傳統的接口。
客戶端調用服務端方法,被調用的方法運行在服務端的Binder線程池中,同時客戶端線程會被掛起,這個時候如果服務端方法執行比較耗時,就會導致客戶端線程長時間阻塞,而如果這個客戶端線程是UI線程的話,就會導致客戶端ANR,所以如果知道服務端的一個方法是耗時的,就要避免在客戶端的UI線程中去調用該遠程方法。
但是有一種方法可以發起非阻塞式的遠程調用:就是在聲明AIDL時,加上oneway關鍵字。
相比平常自定義的 aidl,多了 oneway 的關鍵字,聲明和不聲明 oneway 關鍵字的在於生成 Java 類中一個參數:
不聲明 oneway 時,mRemote.transact 傳入的最後一個參數是 0;聲明 oneway 時,mRemote.transact 傳入的最後一個參數是 android.os.IBinder.FLAG_ONEWAY 。
FLAG_ONEWAY 的作用就是讓客戶端能夠非阻塞的調用遠程方法。
系統Service的調用示例
我們以系統服務的調用來分析AIDL的使用過程。
系統服務的調用過程
首先通過 ServiceManager.getService()方法獲取一個IBinder對象,但是這個IBinder對象不能直接調用,必須要通過asInterface方法轉成對應的IInterface對象纔可以使用,如果在同一個進程中(當然,這是跨進程通信,這種情況很少),就會直接返回通過attachInterface方法設置的IInterface對象(上面代碼所述),但是如果不是在同一個進程中,就會先通過IBinder對象創建一個Proxy對象(服務的本地代理對象),然後在Proxy對象中通過調用IBinder對象的transact方法調用到服務進程的Service中,實現了跨進程的通信。
以AMS爲例,我們來看具體實現。
AMS的服務調用關鍵實現
AIDL 文件生成的類中會自動生成兩個類,Proxy 類和 Stub 類,對應的是 ActivityManagerProxy 和 ActivityManagerService 類。
我們使用ActivityManager實現AMS通信時,在其內部都是調用ActivityManagerNative來實現的。我們來看相關類的繼承關係:
我們在應用層調用,都會調用到ActivityManagerNative.getDefault()方法,我們來看源碼:
static public IActivityManager asInterface(IBinder obj) {
if (obj == null) {
return null;
}
IActivityManager in =
(IActivityManager)obj.queryLocalInterface(descriptor);
if (in != null) {
return in;
}
return new ActivityManagerProxy(obj);
}
static public IActivityManager getDefault() {
return gDefault.get();
}
private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
protected IActivityManager create() {
IBinder b = ServiceManager.getService("activity");
if (false) {
Log.v("ActivityManager", "default service binder = " + b);
}
IActivityManager am = asInterface(b);
if (false) {
Log.v("ActivityManager", "default service = " + am);
}
return am;
}
};
邏輯解析:
- getDefault返回一個Singleton對象。
- ServiceManager.getService(“activity”)返回AMS的IBinder對象,該對象需要轉換後使用。
- asInterface()方法把AMS的IBinder對象轉換爲可供使用的對象。
asInterface()方法調用結果會有2種情況,第一種是,如果調用者和被調用服務在同一個進程,則進行本地通信,直接返回當前對象即可;第二種是,如果調用者和被調用服務是在不同進程,則會通過服務的BinderProxy實現跨進程調用。我們分別來分析。
同一個進程
queryLocalInterface 函數如果不爲null,則在同一進程中通信。
queryLocalInterface 函數:
public IInterface queryLocalInterface(String descriptor) {
if (mDescriptor.equals(descriptor)) {
return mOwner;
}
return null;
}
我們來看mOwner是誰?
public void attachInterface(IInterface owner, String descriptor) {
mOwner = owner;
mDescriptor = descriptor;
}
public ActivityManagerNative() {
attachInterface(this, descriptor);
}
mOwner是在ActivityManagerNative創建的時候賦值的,就是一個ActivityManagerNative對象。AMS的初始化是在SystemServer進程中,所以ActivityManagerNative構造函數是在SystemServer進程中調用的。
不同進程
如果queryLocalInterface 方法返回的是 null,就會創建一個ActivityManagerProxy對象(參數是IBinder對象),該對象是服務端AMS的代理對象,可跨進程通信。
return new ActivityManagerProxy(obj);
我們來看ActivityManagerProxy的部分實現:
class ActivityManagerProxy implements IActivityManager
{
public ActivityManagerProxy(IBinder remote)
{
mRemote = remote;
}
public IBinder asBinder()
{
return mRemote;
}
public int startActivity(IApplicationThread caller, String callingPackage, Intent intent,
String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, ProfilerInfo profilerInfo, Bundle options) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
...
mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);
reply.readException();
...
}
}
可以看到,ActivityManagerProxy 的作用只是作爲代理而已,通過它,最終會調遠程(Service端)mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0)來實現具體的功能。
到了這裏,我們明白,如果是遠程調用,即Client和Server不在同一個進程中,queryLocalInterface必然返回null,這樣就會new一個ActivityManagerProxy對象來進行遠程調用。