前言:回顧一下上一篇博文,上一篇說到,Binder是Android系統中IPC機制的底層依賴,描繪了Binder這種通信架構的基本原理圖,并且談到了使用Binder時需要解決的兩個問題。即:1.用戶端如何擷取Binder對象的引用。2.如何協商服務端的函數辨別和參數放入包裹的順序問題。以便于用戶端調用服務時,服務端能識别并提供正确的服務。通過上一篇的介紹,相信大家已經知道了用戶端通過Service來擷取Binder引用的方式,并且也了解了通過重寫服務端Binder的onTransact()方法和用戶端Binder的transact()方法來解決上述的第二個問題。對于有經驗的程式員,完全可以通過這兩種做法來實作IPC通信。于此同時,Google的大神們也為開發者提供了更為友善的AIDL工具,這個工具可以幫我們解決第二個問題,這樣,開發者就可以完全專注于業務本身。俗話說:“不做重複的輪子”,既然有這種工具,何樂而不用呢?接下來讓我們來走進AIDL。之是以将AIDL放在Binder這裡講,是因為這裡着重分析AIDL如何解決Binder通信中的第二個問題。若是這裡感覺有點迷糊的話,可以看看上一篇博文《Android中的Binder(一)》
一、什麼是AIDL?
AIDL,全稱為Android Interface Definition Language,即android接口定義語言。顧名思義,AIDL隻是一種定義語言,用它的規範定義的接口,可以被aidl工具識别并生成對應的類。(aidl工具在sdk中,可以自行檢視)。相信這樣比較抽象,接下來我們通過一個小小的例子來看看aidl如何工作。
二、一個簡單的AIDL通信示例
服務端代碼,注意與上一篇中的服務端代碼進行比較,差別已經在注釋中展現出來了。
public class ServerService extends Service {
/**
* 建立服務端binder對象(不用aidl時需要自定義Binder子類,并在Service中傳回)
* 并且我們不用在服務端自行書寫onTransact()方法
*/
private Binder serverBinder = new IMyServerService.Stub() {
@Override
public void service2(String args) throws RemoteException {
System.out.println("---service2---:" + args);
}
@Override
public void service1() throws RemoteException {
System.out.println("---service1---");
}
};
@Override
public void onCreate() {
Log.i("DEBUG", "---onCreate()---");
super.onCreate();
}
@Override
public IBinder onBind(Intent intent) {
Log.i("DEBUG", "---onBind()---");
return serverBinder;
}
@Override
public boolean onUnbind(Intent intent) {
Log.i("DEBUG", "---onUnbind()---");
return super.onUnbind(intent);
}
}
aidl接口定義,建立com.example.aidl包,建立IMyServerService接口(自行命名),點選儲存,可以在gen目錄下找到eclipse幫我們自動生成的類IMyServerService.java。之是以建立包,是因為這樣等會可以直接将整個包拷貝到用戶端中。
package com.example.aidl;
interface IMyServerService{
void service1();
void service2(String args);
}
用戶端代碼,同樣注意與上一篇的比較,同樣展現在注釋中。
public class ClientActivity extends Activity {
private Button btn_connectService;
private Button btn_disconnectService;
private Button btn_getService1;
private Button btn_getService2;
// 服務端Service的action,根據自己而變
private Intent intent = new Intent("com.wwt.server.ServerService");
private IMyServerService mService;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 綁定服務端
btn_connectService = (Button) findViewById(R.id.btn_connectService);
btn_connectService.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
bindService(intent, connection, Context.BIND_AUTO_CREATE);
}
});
// 取消綁定服務
btn_disconnectService = (Button) findViewById(R.id.btn_disconnectService);
btn_disconnectService.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
unbindService(connection);
}
});
// 擷取服務端服務1。在沒有aidl的時候,用戶端需要編寫transact()方法,
// 反之則不用。aidl生成的類自動為我們屏蔽了這一細節,即為我們實作了transact()函數。
btn_getService1 = (Button) findViewById(R.id.btn_getService1);
btn_getService1.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
try {
mService.service1();
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
// 擷取服務端服務2
btn_getService2 = (Button) findViewById(R.id.btn_getService2);
btn_getService2.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
try {
mService.service2("Hello");
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
}
/**
* 通過回調擷取Binder對象(可能是服務端的,也可能是Binder驅動中的Binder對象, 取決于本地或是遠端調用。)
*/
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// IMyServerService的内部類Stub的asInterface()方法會根據用戶端擷取到的
// Binder對象的來源來決定調用用戶端服務的方式,即是否采用代理。
mService =IMyServerService.Stub.asInterface(service);
}
};
}
服務端不需要界面直接運作,用戶端界面比較簡單就不寫出來了。
運作結果如下:
工作是正常的。
三、AIDL如何解決前言中提到的Binder通信的第二個問題
打開gen目錄,找到aidl工具為我們生成的類,我們發現這個類名字和我們定義的接口名稱一緻,并且繼承了IInterface接口。接下來我們隻看關鍵的代碼。
public static abstract class Stub extends android.os.Binder implements
com.example.aidl.IMyServerService {
private static final java.lang.String DESCRIPTOR = "com.example.aidl.IMyServerService";
/** Construct the stub at attach it to the interface. */
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.example.aidl.IMyServerService
* interface, generating a proxy if needed.
*/
public static com.example.aidl.IMyServerService asInterface(
android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.example.aidl.IMyServerService))) {
return ((com.example.aidl.IMyServerService) iin);
}
return new com.example.aidl.IMyServerService.Stub.Proxy(obj);
}
@Override
public android.os.IBinder asBinder() {
return this;
}
@Override
public boolean onTransact(int code, android.os.Parcel data,
android.os.Parcel reply, int flags)
throws android.os.RemoteException {
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_service1: {
data.enforceInterface(DESCRIPTOR);
this.service1();
reply.writeNoException();
return true;
}
case TRANSACTION_service2: {
data.enforceInterface(DESCRIPTOR);
java.lang.String _arg0;
_arg0 = data.readString();
this.service2(_arg0);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.example.aidl.IMyServerService {
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
@Override
public android.os.IBinder asBinder() {
return mRemote;
}
public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}
@Override
public void service1() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_service1, _data, _reply,
0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
@Override
public void service2(java.lang.String args)
throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(args);
mRemote.transact(Stub.TRANSACTION_service2, _data, _reply,
0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
}
static final int TRANSACTION_service1 = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_service2 = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
需要注意的有以下這些點:
- 我們看到Stub類繼承自Binder類,是以其本質也是一個Binder類,這也是我們能将其當成服務端Binder傳回的原因。其次Stub類實作了我們在aidl檔案中定義的接口,但是它本身是一個抽象類,并未為我們提供接口方法的實作,是以我們需要在服務端建立的Binder(Stub)對象的時候提供接口方法的具體實作,這便是該服務端提供的服務。
- 接着我們看到Stub類中“最重要”的一個方法asInterface()。不難發現,這個方法在用戶端被調用,用戶端同時會将擷取到的Binder對象傳入這個方法中。是以在asInterface()中會進行一次判斷,若是發現從用戶端傳過來的Binder對象是服務端的(queryLocalInterface()方法),則直接向上轉型為IMyServerService類型并傳回。若是發現從用戶端傳過來的Binder對象是Binder驅動中的,那麼就需要用到Stub的Proxy代理子類了。
- 仔細看Proxy代理類,它在asInterface()方法判斷為遠端調用時被執行個體化并傳回給用戶端。那麼為什麼需要代理類呢?為什麼不能像本地Binder那樣直接向上轉型為IMyServerService呢?其實這樣是行不通的,因為就像上篇說的,遠端調用用戶端獲得的Binder對象是Binder驅動中的,需要注意的是,雖然服務端的Binder(Stub)對象實作了IMyServerService接口,可以正确轉型,但是Binder驅動中的Binder對象并不能實作這種轉型!是以我們需要代理,讓它為我們操縱這個Binder對象,調用其transact()方法。
- 到這裡相信很多人已經發現了,aidl工具為我們生成的Stub類裡面重載了本該在服務端實作的onTransact()方法和本該在用戶端實作的transact()方法,這兩個方法就是決定方法調用的。是以我們說aidl為我們統一了放入參數的順序和函數辨別的問題!
可能大家對于上述的第三點胡有點疑慮,即代理類真的有必要存在嗎的問題,下面進行簡單的驗證。将CilentActivity作一點小小的修改,改動如下,直接将用戶端擷取到的Binder對象進行轉型。
<span style="font-size:14px;">mService =IMyServerService.Stub.asInterface(service);</span>
改為:
mService =(IMyServerService) service
運作結果如下:(類型轉換錯誤,得證)
四、總結
看到這裡,相信大家對于Binder有了比較深的了解了,對AIDL也有了比較淺的認識。利用AIDL可以實作的功能遠不及此,有機會下次在讨論。同時對于Binder通信機制來說,AIDL也不是必須的,我們不僅可以像上一篇說的那樣,自己重寫onTransact()和transact()方法來實作,甚至你可以完全不需要aidl檔案,直接寫出一個類似于IMyServerService的java類(aidl工具隻是為我們生成這個類而已)。 最後在附上aidl中支援的類型:
- 基本資料類型
- String和CharSequence
- List(元素需要時aidl支援的)
- Map(元素需要是aidl支援的)
- AIDL
- Parcelable