Androidプロセス間通信(2)Binder

8465 ワード

紹介する
BinderはIBinderインタフェースを実現するクラスであり、IBinderによって定義されたリモート・プロシージャ呼び出しメカニズムの核心であるAndroidのBinderベースのプロセス間通信メカニズムである.Android Frameworkでは、BinderはServiceManagerが各種のManagerと対応するManagerServiceを接続する架け橋であり、Activityを起動する際にActivity Managementを通過するgetService().startActivityがAMSをリモートで呼び出す方法.アプリケーション層では、Binderはクライアントとサービス側の通信の媒体であり、サービスをバインドすると、サービス側はサービス側のビジネスコールを含むBinderオブジェクトを返し、クライアントでサービス側のメソッドをリモートで呼び出すことができる.
Binderを独自に実装することでプロセス間通信を完了できますが、AndroidはAIDLが必要なBinderクラスを自動的に生成できることを提供しています.それでは、AIDLを定義することで生成されたBinderを理解します.
// Book.aidlpackage com.m1ku.ipcdemo.aidl;
  parcelable Book;
  
// IBookManager.aidl
package com.m1ku.ipcdemo.aidl;
import com.m1ku.ipcdemo.aidl.Book;

interface IBookManager {
    void addBook(in Book book);
    List getBookList();
}

AIDLインタフェースを定義し、コンパイル後に生成されるクラスは次のとおりです.
package com.m1ku.ipcdemo.aidl;
public interface IBookManager extends android.os.IInterface
{       
    /** Local-side IPC implementation stub class. */
    public static abstract class Stub extends android.os.Binder implements                                                      com.m1ku.ipcdemo.aidl.IBookManager
{
    private static final java.lang.String DESCRIPTOR = "com.m1ku.ipcdemo.aidl.IBookManager";
    /** Construct the stub at attach it to the interface. */
        public Stub()
        {
            this.attachInterface(this, DESCRIPTOR);
        }
    /**
     * Cast an IBinder object into an com.m1ku.ipcdemo.aidl.IBookManager interface,
     * generating a proxy if needed.
     */
    public static com.m1ku.ipcdemo.aidl.IBookManager asInterface(android.os.IBinder obj)
    {
        if ((obj==null)) {
        return null;
        }
        android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin!=null)&&(iin instanceof com.m1ku.ipcdemo.aidl.IBookManager))) {
        return ((com.m1ku.ipcdemo.aidl.IBookManager)iin);
        }
    return new com.m1ku.ipcdemo.aidl.IBookManager.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
    {
        java.lang.String descriptor = DESCRIPTOR;
        switch (code)
            {
                case INTERFACE_TRANSACTION:
            {
                reply.writeString(descriptor);
                return true;
            }
            case TRANSACTION_addBook:
            {
                data.enforceInterface(descriptor);
                com.m1ku.ipcdemo.aidl.Book _arg0;
            if ((0!=data.readInt())) {
                _arg0 = com.m1ku.ipcdemo.aidl.Book.CREATOR.createFromParcel(data);
            }
            else {
                _arg0 = null;
            }
                this.addBook(_arg0);
                reply.writeNoException();
                return true;
            }
            case TRANSACTION_getBookList:
            {
                data.enforceInterface(descriptor);
                java.util.List _result = this.getBookList();
                reply.writeNoException();
                reply.writeTypedList(_result);
                return true;
            }
            default:
            {
            return super.onTransact(code, data, reply, flags);
            }
        }
    }
    private static class Proxy implements com.m1ku.ipcdemo.aidl.IBookManager
    {
        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 addBook(com.m1ku.ipcdemo.aidl.Book book) throws android.os.RemoteException
            {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                if ((book!=null)) {
                    _data.writeInt(1);
                    book.writeToParcel(_data, 0);
                }
                else {
                    _data.writeInt(0);
                }
                    mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
                    _reply.readException();
                }
                finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
        @Override public java.util.List getBookList() throws android.os.RemoteException
        {
            android.os.Parcel _data = android.os.Parcel.obtain();
            android.os.Parcel _reply = android.os.Parcel.obtain();
            java.util.List _result;
            try {
                _data.writeInterfaceToken(DESCRIPTOR);
                mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
                _reply.readException();
                _result = _reply.createTypedArrayList(com.m1ku.ipcdemo.aidl.Book.CREATOR);
            }
            finally {
            _reply.recycle();
            _data.recycle();
            }
            return _result;
        }
    }
    static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    }
public void addBook(com.m1ku.ipcdemo.aidl.Book book) throws android.os.RemoteException;
public java.util.List getBookList() throws android.os.RemoteException;
}

構成は次のとおりです.
  • IBookManagerインタフェースはIinterfaceインタフェースを継承し、AIDLインタフェースで定義されたメソッドが宣言されています.
  • StubはBinder実装IBookManagerインタフェースを継承し、IBookManagerの静的内部抽象クラスであり、インタフェースメソッドを識別する整形定数値を定義する.
  • ProxyはIBookManagerインタフェースを実現し、Stubの静的内部クラスであり、IBookManagerの方法を実現している.

  • 各方法の役割asInterface(IBinder obj)
    受信したサービス側IBinderオブジェクトを、クライアントが必要とするAIDLインタフェースタイプのオブジェクト、すなわちAIDLが生成したIBookManagerタイプのオブジェクトに変換します.同じプロセスがStubを返すと、異なるプロセスはProxyエージェントオブジェクトを返します.asBinder
    現在のBinderオブジェクトを返します.getInterfaceDescriptor
    Binderの一意の識別子DESCRIPTORを取得します.一般的にBinderのクラス名です.
    クライアントのプロセス間アクセス手順の説明
    クライアントバインドサービスが成功すると、サービス側から返されるBinderオブジェクトが取得され、クライアントがStubを呼び出すasInterfaceメソッドはAIDLインタフェースタイプのオブジェクトが取得され、プロセス間であるため、Proxyエージェントオブジェクトが取得される.このときクライアントが呼び出すのはProxyのメソッドで、addBookメソッドを例にとると、このメソッドはクライアントで実行され、メソッドではまずシーケンス化されて参照されます.DataでmRemoteを呼び出します.TransactメソッドはRPCリモートコールを開始し、リモート実行が終了して結果を返すまでクライアントスレッドが保留します.Transactメソッドでは、Binderスレッドプールで実行されるStubのonTransactメソッドが呼び出され、ここではcodeに従って対応するメソッドが呼び出され、ここではサービス側のServiceで実装される.RPCプロシージャが返されると、現在のスレッドが実行され続け、RPCが返された結果がreplyから取り出され、最後に返され、リモートコールが終了します.
    RPCリモートコールの場合、現在のスレッドが保留されるため、サブスレッドでRPCリモートコールを開始します.そうしないと、サービス側に時間のかかるタスクがある場合、ANRが発生します.
    死亡エージェント
    Binderはサービス側プロセスで実行されるため、サービス側プロセスが終了すると、サービス側へのリンクが切れ、つまりBinderが死亡します.この場合、リモートコールは無効になりますが、デッドエージェントDeathRecipientを使用するとBinderデッドコールが受信され、接続リクエストを再開始できます.
     private Binder.DeathRecipient deathRecipient = new IBinder.DeathRecipient() {
            @Override
            public void binderDied() {
                if (bookManager!=null){
                    bookManager.asBinder().unlinkToDeath(this,0);
                    bookManager = null;
                }
            }
        };
    

    binderDiedコールバックを受信すると、元のバインドされたデッドエージェントを削除し、リモート・サービスを再バインドします.
    クライアントバインドサービスが成功した場合、BinderのlinkToDeathを呼び出してBinderのデッドエージェントを設定します.
    private ServiceConnection mServiceConnection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
                try {
                    bookManager = IBookManager.Stub.asInterface(iBinder);
                    iBinder.linkToDeath(deathRecipient,0);
                    bookManager.addBook(null);
                    bookManager.getBookList();
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }