2015年2月2日 星期一

Using Binder To Vibrate - Android Binder 實戰 (Client Side, Second Part)

    在第一部分,我們幾乎沒有提到 AIDL 的事情,也沒有解釋為什麼 IMyVibrator 要寫成那樣,在這部分就要為大家來揭曉。

    在講 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 檔。
(綠色的那個 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 是一樣的,就可以跟他通訊。

Summary(謎之音:本篇重點似乎只是展示編譯完、排版好的 AIDL(笑))

    這篇文章希望透過將 Java 的 Binder API 與 Native 的 Binder API 互相呼應對照,讓您能夠對這個在 AOSP 中佔有重要地位的 IPC 系統有更透徹的了解,進而可以靈活的運用。


沒有留言:

張貼留言