Android跨進程通訊基礎——Binder, BinderProxy, parcel, parcelable, Stub, Stub.Proxy(該文章最先於2013年6月7日發表於有道雲筆記 進入閱讀)java
百度、google 過不少文章,都沒能找到可以從 API 使用者角度簡單描述 Binder,BinderProxy,Parcel,Parcelable,Stub,Stub.Proxy 之間關係的文章,要麼高深莫測,要麼混亂不清。最終決定仍是本身動手,看源碼,看文檔,現總結以下:android
Binder,BinderProxy 造成了進程間通訊的基礎,至關於公路橋樑;ide
Parcel 在 IBinder 基礎上傳輸數據,至關於運輸工具;工具
Parcelable 基本數據類型和實現了 Parcelable 接口的複合數據類型纔可被 Parcel 傳輸,至關於擺放整齊、安檢合格的貨物;this
Stub,Stub.Proxy 實現跨進程調用的接口,至關於收發貨方。google
注:Binder,BinderProxy 都實現了 IBinder 接口spa
下面以 Activity 與 Service 通訊爲例來分析其運行機制。代理
示例目的:經過跨進程調用方式在 Activity 進程端調用 Service 進程端的方法。這些方法由接口 RemoteSSO 定義。這裏假設二者是運行在不一樣進程的(也能夠運行在相同進程,具體因配置而不一樣)。code
代碼實現對象
一、定義 RemoteSSO.aidl 文件:
package com.sina.sso; interface RemoteSSO { String getPackageName(); String getActivityName(); }
二、ADT會在gen目錄下生成RemoteSSO.java文件以下:
/* * This file is auto-generated. DO NOT MODIFY. * Original file: D:\\workspace_android\\IeltsPraBook\\src\\com\\sina\\sso\\RemoteSSO.aidl */ package com.sina.sso; public interface RemoteSSO extends android.os.IInterface { /** Local-side IPC implementation stub class. */ public static abstract class Stub extends android.os.Binder implements com.sina.sso.RemoteSSO { private static final java.lang.String DESCRIPTOR = "com.sina.sso.RemoteSSO"; /** Construct the stub at attach it to the interface. */ public Stub() { this.attachInterface(this, DESCRIPTOR); } /** * Cast an IBinder object into an com.sina.sso.RemoteSSO interface, * generating a proxy if needed. */ public static com.sina.sso.RemoteSSO asInterface(android.os.IBinder obj) { if ((obj==null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin!=null)&&(iin instanceof com.sina.sso.RemoteSSO))) { return ((com.sina.sso.RemoteSSO)iin); } return new com.sina.sso.RemoteSSO.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 { switch (code) { case INTERFACE_TRANSACTION: { reply.writeString(DESCRIPTOR); return true; } case TRANSACTION_getPackageName: { data.enforceInterface(DESCRIPTOR); java.lang.String _result = this.getPackageName(); reply.writeNoException(); reply.writeString(_result); return true; } case TRANSACTION_getActivityName: { data.enforceInterface(DESCRIPTOR); java.lang.String _result = this.getActivityName(); reply.writeNoException(); reply.writeString(_result); return true; } } return super.onTransact(code, data, reply, flags); } private static class Proxy implements com.sina.sso.RemoteSSO { 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 java.lang.String getPackageName() throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); java.lang.String _result; try { _data.writeInterfaceToken(DESCRIPTOR); mRemote.transact(Stub.TRANSACTION_getPackageName, _data, _reply, 0); _reply.readException(); _result = _reply.readString(); } finally { _reply.recycle(); _data.recycle(); } return _result; } @Override public java.lang.String getActivityName() throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); java.lang.String _result; try { _data.writeInterfaceToken(DESCRIPTOR); mRemote.transact(Stub.TRANSACTION_getActivityName, _data, _reply, 0); _reply.readException(); _result = _reply.readString(); } finally { _reply.recycle(); _data.recycle(); } return _result; } } static final int TRANSACTION_getPackageName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); static final int TRANSACTION_getActivityName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1); } public java.lang.String getPackageName() throws android.os.RemoteException; public java.lang.String getActivityName() throws android.os.RemoteException; }
三、定義 MyRemoteSSO extends RemoteSSO.Stub。因爲 RemoteSSO.Stub 是抽象類,咱們應該繼承該類並實現相應的功能。這裏是如下兩方法:
public java.lang.String getPackageName() throws android.os.RemoteException; public java.lang.String getActivityName() throws android.os.RemoteException;
四、在 Service 的 onBind() 方法裏面返回 MyRemoteSSO 對象。
五、在 Activity 中調用 bindService(),並在鏈接後經過如下轉換就能夠與另外一個進程中的 Service 通訊,調用相關方法了。
ServiceConnection mServConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder binder) { RemoteSSO mInterface = RemoteSSO.Stub.asInterface(binder); ... mInterface. getPackageName(); mInterface. getActivityName(); ... } }
至此,通道構造完畢,能夠進行跨進程通訊了。
運行機制
一、綁定 Service,創建跨進程通訊通道
a、Activity 中調用 context.bindService(),Android 經過系統服務來啓動或者鏈接到 Service;
b、系統服務通知 Service 回調 onBind() 方法返回 Binder 對象的一個實例(Binder 對象事實上是 RemoteSSO.Stub 的子類對象,而 RemoteSSO.Stub 繼承自 Binder);
c、系統服務通知 Activity 進程端生成一個對應的 BinderProxy 對象,並做爲 onServiceConnected(ComponentName name, IBinder binder) 方法的回調參數 binder,此時咱們須要將 binder 做爲參數手動調用 RemoteSSO.Stub. asInterface(binder) 來獲取一個實現了 RemoteSSO 接口的代理對象 proxy 以供咱們調用,此時通道創建完成,咱們能夠經過 proxy 來調用 Service 端的方法了。
因爲 proxy 是實現了自定義接口 RemoteSSO 的,所以外部看來只是調用了接口方法而不是代理。事實上,proxy 是一個 RemoteSSO.Stub.Proxy 對象的實例,參數 binder(BinderProxy對象實例)便做爲了 proxy 的一個變量備用。
Binder 和 BinderProxy 是成對的,在進程間用 descriptor 來標記對應關係,descriptor 一般是 RemoteSSO 的全路徑名,在自動生成的 RemoteSSO.java 接口中已經自動生成。同時 Stub 和 Stub.Proxy 也是成對的,Service 端返回的是繼承了 Binder 的 Stub 的子類對象實例,而 Activity 端用來實現調用的接口實例是將 BinderProxy 做爲變量的 Stub.Proxy 對象的實例。
二、調用與返回
畢竟,咱們的最終目的是通訊,這裏就是調用方法。那麼當咱們拿到 RemoteSSO 接口的實例 proxy,調用 getPackageName() 的時候,執行過程是怎樣的呢?
a、因爲 proxy 是 RemoteSSO.Stub.Proxy 對象的實例,則 proxy.getPackageName() 即是 RemoteSSO.Stub.Proxy 的 getPackageName()。
@Override public java.lang.String getPackageName() throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); java.lang.String _result; try { _data.writeInterfaceToken(DESCRIPTOR); mRemote.transact(Stub.TRANSACTION_getPackageName, _data, _reply, 0); _reply.readException(); _result = _reply.readString(); } finally { _reply.recycle(); _data.recycle(); } return _result; }
該代碼是自動生成的。注意這裏的 mRemote 就是 BinderProxy。
b、代理對象 RemoteSSO.Stub.Proxy 將方法的參數打包到 Parcel 對象的實例 _data 裏,並由 BinderProxy 調用 transact()(見 mRemote.transact(Stub.TRANSACTION_getPackageName, _data, _reply, 0); )經過底層系統級跨進程機制通知 Service 端的 Binder(Service.onBind() 返回的 RemoteSSO.Stub 子類對象)調用 transact(int code, Parcel data, Parcel reply, int flags);注意方法名是經過 code 參數標記的,見常量 Stub.TRANSACTION_getPackageName。
c、Binder.transact() 回調 RemoteSSO.Stub 的 onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags),進而調用自定義接口方法,並將返回值寫入到 reply 進行打包並返回。這裏是 getPackageName(),該自定義方法是手動在 RemoteSSO.Stub 的子類中實現的(具體要作什麼只有本身知道,ADT 不可能幫你生成)。
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException { switch (code) { case INTERFACE_TRANSACTION: { reply.writeString(DESCRIPTOR); return true; } case TRANSACTION_getPackageName: { data.enforceInterface(DESCRIPTOR); java.lang.String _result = this.getPackageName(); reply.writeNoException(); reply.writeString(_result); return true; } case TRANSACTION_getActivityName: { data.enforceInterface(DESCRIPTOR); java.lang.String _result = this.getActivityName(); reply.writeNoException(); reply.writeString(_result); return true; } } return super.onTransact(code, data, reply, flags); }
注意這裏根據 code 對方法名的標記找到對應的實際要調用的方法。
d、Binder.transact()、BinderProxy.transact() 一層層返回,即 RemoteSSO.Stub.Proxy.getPackageName() 方法的 mRemote.transact(Stub.TRANSACTION_getPackageName, _data, _reply, 0) 方法返回,此時 _reply 已經在 Service 進程中被填充了返回值,接下來能夠讀取該值並返回了。
至此,整個調用過程結束。
FAQ
你們有沒有想過,若是調用發生在同一個進程會發生什麼?
public void onServiceConnected(ComponentName name, IBinder binder) { RemoteSSO mInterface = RemoteSSO.Stub.asInterface(binder); ... } public static com.sina.sso.RemoteSSO asInterface(android.os.IBinder obj) { if ((obj==null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin!=null)&&(iin instanceof com.sina.sso.RemoteSSO))) { return ((com.sina.sso.RemoteSSO)iin); } return new com.sina.sso.RemoteSSO.Stub.Proxy(obj); }
事實上,在同一個進程中 onServiceConnected() 的參數 binder 是 Binder 對象而不是 BinderProxy 對象,所以在執行 asInterface() 的時候返回的是 binder 自己(見obj.queryLocalInterface(DESCRIPTOR);),也就是 RemoteSSO.Stub 子類對象,而不是 RemoteSSO.Stub.Proxy 對象,此時在執行 getPackageName() 時直接就是調用手動實現的該方法,也就是直接調用了 Service 端的該方法,而沒有繞一個大圈。若是必然是在同一個進程,能夠直接這樣處理來得到調用接口:
public void onServiceConnected(ComponentName name, IBinder binder) { RemoteSSO mInterface = (RemoteSSO)binder; ... }