1. 程式人生 > >Android進階筆記:AIDL內部實現詳解 (二)

Android進階筆記:AIDL內部實現詳解 (二)

ucc == 筆記 null stack 直接 android 最好 public

接著上一篇分析的aidl的流程解析。知道了aidl主要就是利用Ibinder來實現跨進程通信的。既然是通過對Binder各種方法的封裝,那也可以不使用aidl自己通過Binder來實現跨進程通訊。那麽這篇博客就主要就寫一下通過上篇(Android進階筆記:AIDL詳解(一))總結的知識來自己實現跨進程通訊從而更加透徹的了解aidl的核心邏輯。

首先上一篇博客(Android進階筆記:AIDL詳解(一))中總結出一個結論————“onTransact方法是提供給server端用的,transact方法(內部類proxy封裝了transact方法)和asInterface方法是給client端用的。”因此很清楚,只要我們在Server端實現跨進程需要調用的方法(類似aidl的接口實現)和onTransact方法,而服務端只要通過獲得的IBinder對象來調用transact方法就可以代替aidl來實現跨進程通訊了。既然思路已經整理清楚了,那就一步一步來實現它。

Server端

首先Server端是要通過Service的onBind方法來給Client端一個Binder對象,那就先從這個Binder對象入手。那就先來創建了一個MyBinder類,代碼如下:

MyBinder.java

public class MyBinder extends Binder {
    //標記方法的
    private static final int METHOD_ADD_CODE = 1001;
    //標識binder對象的
    private static final String DESCRIPTION = "not use aidl";

    @Override
    protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
        if (code == METHOD_ADD_CODE) {
            //驗證一下binder
            data.enforceInterface(DESCRIPTION);
            //從parcel對象中讀取參數
            int arg0 = data.readInt();
            int arg1 = data.readInt();
            //寫入結果
            reply.writeInt(add(arg0, arg1));
            return true;
        }
        return super.onTransact(code, data, reply, flags);
    }

    private int add(int arg0, int arg1) {
        return arg0 + arg1;
    }
}

代碼非常簡單,只是重新寫了一下onTransact方法。其實一共只有4步:

  1. 根據code的值來判斷client端具體想要調用哪個方法;
  2. 讀取parcel對象(data)中傳入的參數;
  3. 調用自己本地的方法(add)並將參數傳入;
  4. 把結果寫入parcel對象(reply)中;

接著只要把這個自己定義的MyBinder類的實例通過Service.onBInder方法返回給Client端就可以了。

MyService.java

public class MyService extends Service {
    private MyBinder myBinder;

    public MyService() {
    }

    @Override
    public void onCreate() {
        super.onCreate();
        //創建實例
        myBinder = new MyBinder();
    }

    @Override
    public IBinder onBind(Intent intent) {
        //返回自定義的binder對象
        return myBinder;
    }
}

Client端

client端的代碼無非就是把之前寫在aidl中的proxy內部類的方法拿出來了。具體看代碼:

WithoutAidlActivity.java

public class WithoutAidlActivity extends AppCompatActivity {

    private ServiceConnection serviceConnection;
    private IBinder binder;

    //以下兩個參數要和server端保持一致
    //標記方法的(告知server端調用哪個方法)
    private static final int METHOD_ADD_CODE = 1001;
    //標識binder對象的
    private static final String DESCRIPTION = "not use aidl";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_without_aidl);
        serviceConnection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                Log.d("onServiceConnected", "onServiceConnected: connected success!");
                binder = service;
                //這裏就代替aidl中的proxy來直接調用transact方法
                //先準備參數
                Parcel data = Parcel.obtain();
                Parcel reply = Parcel.obtain();
                data.writeInterfaceToken(DESCRIPTION);
                data.writeInt(123);
                data.writeInt(456);
                try {
                    //調用transact方法
                    binder.transact(METHOD_ADD_CODE, data, reply, 0);
                    //獲得結果
                    int result = reply.readInt();
                    Log.d("onServiceConnected", "result = " + result);
                } catch (RemoteException e) {
                    e.printStackTrace();
                } finally {
                    data.recycle();
                    reply.recycle();
                }
            }

            @Override
            public void onServiceDisconnected(ComponentName name) {
                binder = null;
            }
        };

        bindService(new Intent("com.coder_f.aidlserver.MyService"), serviceConnection, BIND_AUTO_CREATE);

    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(serviceConnection);
    }
}

首先連接成功後在serviceConnection.onServiceConnected方法中獲得了IBinder實例,然後總共做了3個事情:

  1. 創建兩個parcel對象分別存放參數(data)和返回值(reply)
  2. 調用transact方法,傳入data,reply,和你要調用的方法code。最後的flag傳入0表示有返回值(1表示沒有又返回值)
  3. 從reply中獲得結果

完成以上工作就可以不通過aidl實現跨進程通訊了。但是還是要說一下,這裏我們server端調用的只是一個簡單的add方法不耗時的,而transact方法則是在onServiceConnected方法中被調用的其實是在主線程中執行的。如果add方法換成一個耗時方法,那麽主線程(UI線程)是會卡死的,調用transact方法時當前線程會被掛起知道結果被返回(有興趣可以去試試,只要在add方法裏面加一個Thread.sleep就可以了)。所以最好的辦法就是起一個線程來調用transact方法。

Android進階筆記:AIDL內部實現詳解 (二)