2011年03月22日
AndroidのBinderによるプロセス間のメソッド呼び出し(メモ2:AIDL)
前回のまとめ
前回は frameworks/base/core/java/android/app/ActivityManagerNative.java をみてみましたが、他にも同様な構成のものがいつくかあります。
$ cd frameworks/base/core $ find . -name "*Native.java" ./java/android/content/ContentProviderNative.java ./java/android/os/ServiceManagerNative.java ./java/android/database/BulkCursorNative.java ./java/android/app/ActivityManagerNative.java ./java/android/app/ApplicationThreadNative.java
これらのjavaファイルではそれぞれひとつのinterfaceに対して2つの実装がありました。ひとつはbinderを通して他のプロセスに実行を要求するもの(Proxy)で、もうひとつは他のプロセスからきた要求を受け取って実際のメソッドを実行するもの(Stub)です。
AIDL
interfaceを定義して、それぞれにProxyクラスとStubクラスを作ることは煩雑ですし、ケアレスミスが入り込むとデバッグはかなりやっかいです。
実はAIDL(Android Interface Definition Language)はこれを解決してくれるものです。詳しくは本家のページを参照してください。
AIDLの規約に従って末尾が.aidlとなっているファイルを作成すると、aidlというツールでjavaのファイルを自動生成できるようになっています。
例として IAlarmManager.aidl を見てみます。
frameworks/base/core/java/android/app/IAlarmManager.aidl
package android.app; import android.app.PendingIntent; /** * System private API for talking with the alarm manager service. * * {@hide} */ interface IAlarmManager { void set(int type, long triggerAtTime, in PendingIntent operation); void setRepeating(int type, long triggerAtTime, long interval, in PendingIntent operation); void setInexactRepeating(int type, long triggerAtTime, long interval, in PendingIntent operation); void setTime(long millis); void setTimeZone(String zone); void remove(in PendingIntent operation); }
このIAlarmManager.aidl から IAlarmManager.java が生成されます。(outディレクトリ以下にあります。)
IAlarmManager.javaは以下のようなinterfaceになっていて、それは2つの内部クラスを持っています。
public interface IAlarmManager extends IInterface { public static abstract class Stub extends Binder implements IAlarmManager { ... } private static class Proxy implements IAlarmManger { ... } }
これらのファイルは前回みたActivityManagerNative.javaとよく似ています。abstact classであるIAlarmManager.Stub はcom.android.server.AlarmManagerService が継承していました。
frameworks/base/services/java/com/android/server/AlarmManagerService.java
実際のIAlarmManager.java
生成されたIAlarmManager.javaの実際のファイルの抜粋は以下の通りです。(見やすいようにインデントをつけてあります。)
前回のファイルと比較してみてください。
/* * This file is auto-generated. DO NOT MODIFY. * Original file: frameworks/base/core/java/android/app/IAlarmManager.aidl */ package android.app; /** * System private API for talking with the alarm manager service. * * {@hide} */ public interface IAlarmManager extends android.os.IInterface { /** Local-side IPC implementation stub class. */ public static abstract class Stub extends android.os.Binder implements android.app.IAlarmManager { private static final java.lang.String DESCRIPTOR = "android.app.IAlarmManager"; /** Construct the stub at attach it to the interface. */ public Stub() { this.attachInterface(this, DESCRIPTOR); } /** * Cast an IBinder object into an android.app.IAlarmManager interface, * generating a proxy if needed. */ public static android.app.IAlarmManager asInterface(android.os.IBinder obj) { if ((obj==null)) { return null; } android.os.IInterface iin = (android.os.IInterface)obj.queryLocalInterface(DESCRIPTOR); if (((iin!=null)&&(iin instanceof android.app.IAlarmManager))) { return ((android.app.IAlarmManager)iin); } return new android.app.IAlarmManager.Stub.Proxy(obj); } 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_set: { data.enforceInterface(DESCRIPTOR); int _arg0; _arg0 = data.readInt(); long _arg1; _arg1 = data.readLong(); android.app.PendingIntent _arg2; if ((0!=data.readInt())) { _arg2 = android.app.PendingIntent.CREATOR.createFromParcel(data); } else { _arg2 = null; } this.set(_arg0, _arg1, _arg2); reply.writeNoException(); return true; } ... } return super.onTransact(code, data, reply, flags); } private static class Proxy implements android.app.IAlarmManager { private android.os.IBinder mRemote; Proxy(android.os.IBinder remote) { mRemote = remote; } public android.os.IBinder asBinder() { return mRemote; } public java.lang.String getInterfaceDescriptor() { return DESCRIPTOR; } public void set(int type, long triggerAtTime, android.app.PendingIntent operation) 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(type); _data.writeLong(triggerAtTime); if ((operation!=null)) { _data.writeInt(1); operation.writeToParcel(_data, 0); } else { _data.writeInt(0); } mRemote.transact(Stub.TRANSACTION_set, _data, _reply, 0); _reply.readException(); } finally { _reply.recycle(); _data.recycle(); } } ... } static final int TRANSACTION_set = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); static final int TRANSACTION_setRepeating = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1); static final int TRANSACTION_setInexactRepeating = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2); static final int TRANSACTION_setTime = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3); static final int TRANSACTION_setTimeZone = (android.os.IBinder.FIRST_CALL_TRANSACTION + 4); static final int TRANSACTION_remove = (android.os.IBinder.FIRST_CALL_TRANSACTION + 5); } public void set(int type, long triggerAtTime, android.app.PendingIntent operation) throws android.os.RemoteException; public void setRepeating(int type, long triggerAtTime, long interval, android.app.PendingIntent operation) throws android.os.RemoteException; public void setInexactRepeating(int type, long triggerAtTime, long interval, android.app.PendingIntent operation) throws android.os.RemoteException; public void setTime(long millis) throws android.os.RemoteException; public void setTimeZone(java.lang.String zone) throws android.os.RemoteException; public void remove(android.app.PendingIntent operation) throws android.os.RemoteException; }
関連するページ
AndroidのBinderによるプロセス間のメソッド呼び出し(メモ)
AndroidのBinderによるプロセス間のメソッド呼び出し(メモ2:AIDL)
AndroidのBinderによるプロセス間のメソッド呼び出し(メモ3:Binder Thread)