標籤:protect parcel connected targe 先來 content nec method return
接著上一篇分析的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內部實現詳解 (二)