Android進階筆記:AIDL內部實現詳解 (二)
接著上一篇分析的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步:
- 根據code的值來判斷client端具體想要調用哪個方法;
- 讀取parcel對象(data)中傳入的參數;
- 調用自己本地的方法(add)並將參數傳入;
- 把結果寫入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個事情:
- 創建兩個parcel對象分別存放參數(data)和返回值(reply)
- 調用transact方法,傳入data,reply,和你要調用的方法code。最後的flag傳入0表示有返回值(1表示沒有又返回值)
- 從reply中獲得結果
完成以上工作就可以不通過aidl實現跨進程通訊了。但是還是要說一下,這裏我們server端調用的只是一個簡單的add方法不耗時的,而transact方法則是在onServiceConnected方法中被調用的其實是在主線程中執行的。如果add方法換成一個耗時方法,那麽主線程(UI線程)是會卡死的,調用transact方法時當前線程會被掛起知道結果被返回(有興趣可以去試試,只要在add方法裏面加一個Thread.sleep就可以了)。所以最好的辦法就是起一個線程來調用transact方法。
Android進階筆記:AIDL內部實現詳解 (二)