在講 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 系統有更透徹的了解,進而可以靈活的運用。
沒有留言:
張貼留言