在講 AIDL 之前,先來提一下 Android 的 Java Service 吧,或者更明確的說,bindService() 這個函式與相關的東西。我剛開始學寫 Service 時,常常困惑:假如我要跟 Service 溝通,不就弄一個 public 的變數或函式,讓雙方可以互相存取,為什麼還莫名其妙地跑出一個 onBind() 以及 bindService() 呢?的確,如果是在同一個 package 裏,是可以這麼做的,原因很簡單:在這個情況下,Service 跟主 App 是在同一個 thread。到這邊應該可以大概想出來,常與 bind 相關一起提到的 AIDL 是用在什麼情況了吧?沒錯,就是跨進程的 Service,所以我才在上一篇說到,AIDL 是 Binder 的 Java 綁定版本相關事物。
AIDL 真面目
事實上 AIDL 檔在 App 的 Build 過程中,會編譯成一個 Java 檔,例如這次例子中的 IVibratorService.aidl ( 位於 frameworks/base/core/java/android/os/ ):
package android.os;
/** {@hide} */
interface IVibratorService
{
boolean hasVibrator();
void vibrate(int uid, String opPkg, long milliseconds, int usageHint, IBinder token);
void vibratePattern(int uid, String opPkg, in long[] pattern, int repeat, int usageHint, IBinder token);
void cancelVibrate(IBinder token);
}
我們把內容複製下來,改一下 package,貼到一個一樣名字的 AIDL 檔,並且執行有關 AIDL 的 gradle 任務,如下圖所示:
接下來會在 app/build/generated/source/aidl 底下看到生成的 Java 檔。
接下來會在 app/build/generated/source/aidl 底下看到生成的 Java 檔。
(綠色的那個 IVibratorService)
把它打開來看,以下是已經排版過的。(盡量不要修改該檔案)
package com.test.vibratorbinder;
// Declare any non-default types here with import statements
public interface IVibratorService extends android.os.IInterface {
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.test.vibratorbinder.IVibratorService {
private static final java.lang.String DESCRIPTOR = "com.test.vibratorbinder.IVibratorService";
/** Construct the stub at attach it to the interface. */
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.test.vibratorbinder.IVibratorService interface,
* generating a proxy if needed.
*/
public static com.test.vibratorbinder.IVibratorService asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.test.vibratorbinder.IVibratorService))) {
return ((com.test.vibratorbinder.IVibratorService) iin);
}
return new com.test.vibratorbinder.IVibratorService.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_hasVibrator:
{
data.enforceInterface(DESCRIPTOR);
boolean _result = this.hasVibrator();
reply.writeNoException();
reply.writeInt(((_result) ? (1) : (0)));
return true;
}
case TRANSACTION_vibrate:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
java.lang.String _arg1;
_arg1 = data.readString();
long _arg2;
_arg2 = data.readLong();
int _arg3;
_arg3 = data.readInt();
android.os.IBinder _arg4;
_arg4 = data.readStrongBinder();
this.vibrate(_arg0, _arg1, _arg2, _arg3, _arg4);
reply.writeNoException();
return true;
}
case TRANSACTION_vibratePattern:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
java.lang.String _arg1;
_arg1 = data.readString();
long[] _arg2;
_arg2 = data.createLongArray();
int _arg3;
_arg3 = data.readInt();
int _arg4;
_arg4 = data.readInt();
android.os.IBinder _arg5;
_arg5 = data.readStrongBinder();
this.vibratePattern(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
reply.writeNoException();
return true;
}
case TRANSACTION_cancelVibrate:
{
data.enforceInterface(DESCRIPTOR);
android.os.IBinder _arg0;
_arg0 = data.readStrongBinder();
this.cancelVibrate(_arg0);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.test.vibratorbinder.IVibratorService {
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 boolean hasVibrator() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
boolean _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_hasVibrator, _data, _reply, 0);
_reply.readException();
_result = (0 != _reply.readInt());
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public void vibrate(int uid, java.lang.String opPkg, long milliseconds, int usageHint, android.os.IBinder token) 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.writeInt(uid);
_data.writeString(opPkg);
_data.writeLong(milliseconds);
_data.writeInt(usageHint);
_data.writeStrongBinder(token);
mRemote.transact(Stub.TRANSACTION_vibrate, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
@Override
public void vibratePattern(int uid, java.lang.String opPkg, long[] pattern, int repeat, int usageHint, android.os.IBinder token)
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.writeInt(uid);
_data.writeString(opPkg);
_data.writeLongArray(pattern);
_data.writeInt(repeat);
_data.writeInt(usageHint);
_data.writeStrongBinder(token);
mRemote.transact(Stub.TRANSACTION_vibratePattern, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
@Override
public void cancelVibrate(android.os.IBinder token) 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.writeStrongBinder(token);
mRemote.transact(Stub.TRANSACTION_cancelVibrate, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
}
static final int TRANSACTION_hasVibrator = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_vibrate = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_vibratePattern = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
static final int TRANSACTION_cancelVibrate = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
}
public boolean hasVibrator() throws android.os.RemoteException;
public void vibrate(int uid, java.lang.String opPkg, long milliseconds, int usageHint, android.os.IBinder token) throws android.os.RemoteException;
public void vibratePattern(int uid, java.lang.String opPkg, long[] pattern, int repeat, int usageHint, android.os.IBinder token) throws android.os.RemoteException;
public void cancelVibrate(android.os.IBinder token) throws android.os.RemoteException;
}
看來本體是一個 interface,我們先從 Proxy 類開始分析吧。會從這個類開始講不是沒有原因的,往裡面一看:每一個函式的內容,不就是 BpMyVibrator 裡面函式在做的事嗎,像一個代理人一樣,把傳進來的函式參數打包進 Parcel,調用 IBinder 的 transact() 傳送出去,瞧,連名字大部份都一樣呢!之前說 AIDL 一定會丟出例外,所以若要讀取返回值的話,要先把 exceptionCode 讀出來,在這邊也可以得到應證。
再來看看 Stub 類吧。裡面最大的一部分,onTransact(),雖然前一篇沒有介紹,但您應該看得出來他就是接收端的處理函式,而我們在這邊要看的,是 asInterface()。韓式裡面似乎也很好懂,原理甚至命名都跟 IMPLEMENT_META_INTERFACE() 幾乎一模一樣:先看看自己有沒有實作對應的業務的介面,如果沒有的話,就 new 一個 Proxy,角色等同於前一篇的 BpMyVibrator,解讀上完全沒有障礙。
透過剛剛的解析,就可以了解為什麼前一篇的 IMyVibrator 的那些 virtual 函式要長成那樣,然後 descriptor 要設定成那個值了。因為雖然我們要溝通的對方,VibratorService,是活在 system_server 裏的 Java Service,但多虧了 Binder,我們只要業務介面、descriptor 是一樣的,就可以跟他通訊。
再來看看 Stub 類吧。裡面最大的一部分,onTransact(),雖然前一篇沒有介紹,但您應該看得出來他就是接收端的處理函式,而我們在這邊要看的,是 asInterface()。韓式裡面似乎也很好懂,原理甚至命名都跟 IMPLEMENT_META_INTERFACE() 幾乎一模一樣:先看看自己有沒有實作對應的業務的介面,如果沒有的話,就 new 一個 Proxy,角色等同於前一篇的 BpMyVibrator,解讀上完全沒有障礙。
透過剛剛的解析,就可以了解為什麼前一篇的 IMyVibrator 的那些 virtual 函式要長成那樣,然後 descriptor 要設定成那個值了。因為雖然我們要溝通的對方,VibratorService,是活在 system_server 裏的 Java Service,但多虧了 Binder,我們只要業務介面、descriptor 是一樣的,就可以跟他通訊。
Summary(謎之音:本篇重點似乎只是展示編譯完、排版好的 AIDL(笑))
這篇文章希望透過將 Java 的 Binder API 與 Native 的 Binder API 互相呼應對照,讓您能夠對這個在 AOSP 中佔有重要地位的 IPC 系統有更透徹的了解,進而可以靈活的運用。


沒有留言:
張貼留言