為了簡化我們實現Binder程序間通訊的繁瑣步驟,系統提供了AIDL檔案,編譯通過即可生成對應的java檔案。承接上篇文章Android程序間通訊-AIDL的簡單使用 ,這裡手動實現aidl自動生成的aidl檔案,便於我們進一步理解內部的一些邏輯。

實現步驟

1 定義一個IOperation介面,並在內部宣告一個供客戶端跨程序呼叫的方法,並使該介面繼承IInterface介面。

public interface IOperation extends IInterface {

    /* Binder描述符 */
    static final String DESCRIPTOR = "com.example.aidlmanualtest.IOperation";

    /**
     * 方法識別符號 :規定了最小和最大範圍
     * 
     * The action to perform. This should be a number between
     * {@link #FIRST_CALL_TRANSACTION}
     * {@link #LAST_CALL_TRANSACTION}.
     * 
     * 這段說明是在IBiner的transact()方法中
     */

    static final int TRANSACTION_getSum = IBinder.FIRST_CALL_TRANSACTION + 0;
    // 定義了一個供跨程序呼叫的方法
    public double getSum(double first, double second) throws RemoteException;
}

2 跨程序方法呼叫的處理邏輯,主要集中在介面實現類Stub和Stub的代理Proxy類中

public class IOperationStub extends Binder implements IOperation {

    public IOperationStub() {
        /**
         * Convenience method for associating a specific interface with the
         * Binder. After calling, queryLocalInterface() will be implemented for
         * you to return the given owner IInterface when the corresponding
         * descriptor is requested.
         * 
         * 將一個IInterface型別的介面例項與Binder關聯起來,DESCRIPTOR相當於一個標識。當該方法被呼叫後,
         * 可通過queryLocalInterface(DESCRIPTOR)獲取與這個標識相應的介面例項
         */
        this.attachInterface(this, DESCRIPTOR);
    }

    /**
     * 用於將服務端的Binder物件轉換成客戶端需要的AIDL介面型別的物件區分程序:
     * <p/>
     * 客戶端和服務端處於同一程序:直接返回服務端的Stub物件
     * <p/>
     * 客戶端和服務端處於不同程序:返回封裝好的Stub.proxy物件
     * 
     * @param obj
     * @return
     */
    public static IOperation asInterface(IBinder obj) {
        if (obj == null) {
            return null;
        }
        /**
         * Attempt to retrieve a local implementation of an interface for this
         * Binder object. If null is returned, you will need to instantiate a
         * proxy class to marshall calls through the transact() method.
         * 
         * 嘗試在本地檢索實現了該介面的Binder物件例項
         */
        android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
        if (iin != null && (iin instanceof IOperation)) {
            return ((IOperation) iin);
        }
        /**
         * 假如本地檢索返回為null,需要手動生成一個proxy代理類,並在其中通過transact() 方法去處理請求
         */
        return new IOperationStub.Proxy(obj);
    }

    @Override
    public IBinder asBinder() {
        return this;
    }

    /**
     * 該方法執行在服務端,當客戶端發起請求,會進入到該方法進行處理
     * <p/>
     * code:用於標識客戶端請求呼叫的目標方法(即在IOperation中定義的方法識別符號)
     * <p/>
     * data:當請求的目標方法含有引數時,該引數封裝了請求的引數
     * <p/>
     * reply:當請求需要返回結果時,該引數封裝了處理結果
     */
    @Override
    protected boolean onTransact(int code, Parcel data, Parcel reply, int flags)
            throws RemoteException {
        // 通過code區分不同的方法呼叫
        switch (code) {
        case INTERFACE_TRANSACTION:
            reply.writeString(DESCRIPTOR);
            return true;
        case TRANSACTION_getSum:
        /**
             * Read an IBinder interface token in the parcel at the current
             * {@link #dataPosition}. This is used to validate that the
             * marshalled transaction is intended for the target interface.
             */
            data.enforceInterface(DESCRIPTOR);
            // 通過data獲取請求的引數
            double first = data.readDouble();
            double second = data.readDouble();
            double result = this.getSum(first, second);
            reply.writeNoException();
            // 將結果寫入到reply中
            reply.writeDouble(result);
            return true;
        default:
            break;
        }
        return super.onTransact(code, data, reply, flags);
    }

    @Override
    public double getSum(double fisrt, double second) throws RemoteException {
        return 0;
    }

    private static class Proxy implements IOperation {

        private IBinder mRemote;

        Proxy(IBinder mRemote) {
            this.mRemote = mRemote;
        }

        @Override
        public IBinder asBinder() {
            return mRemote;
        }

        public java.lang.String getInterfaceDescriptor() {
            return DESCRIPTOR;
        }

        @Override
        public double getSum(double fisrt, double second)
                throws RemoteException {
            Parcel data = Parcel.obtain();
            Parcel reply = Parcel.obtain();
            double result = 0;
            try {
                /**
                 * Store an IBinder interface token in the parcel at the current
                 * {@link #dataPosition}. This is used to validate that the
                 * marshalled transaction is intended for the target interface.
                 */
                data.writeInterfaceToken(DESCRIPTOR);
                // 將請求的引數寫入到data中
                data.writeDouble(fisrt);
                data.writeDouble(second);
                // 呼叫transact()方法,發起跨程序請求,當前程序掛起
                mRemote.transact(TRANSACTION_getSum, data, reply, 0);
                // 從reply中獲取返回的結果
                reply.readException();
                result = reply.readDouble();
            } catch (Exception e) {
            } finally {
                data.recycle();
                reply.recycle();
            }
            return result;
        }
    }

}

總結

總體邏輯:

1 客戶端呼叫getSum()方法
2 將該方法需要的傳遞的引數寫入到data中
3 呼叫transact()方法發起跨程序請求
4 服務端的onTransact()的方法被呼叫,客戶端的程序被掛起
5 服務端返回結果,從reply中取出結果,並返回