Android 跨進程通信基礎 頂 原

Android跨進程通信基礎——Binder, BinderProxy, parcel, parcelable, Stub, Stub.Proxy(該文章最早於2013年6月7日發表於有道雲筆記 進入閱讀)


      百度、google 過很多文章,都沒能找到能夠從 API 使用者角度簡單描述 Binder,BinderProxy,Parcel,Parcelable,Stub,Stub.Proxy 之間關係的文章,要麼高深莫測,要麼混亂不清。最終決定還是自己動手,看源碼,看文檔,現總結如下:


      Binder,BinderProxy 形成了進程間通信的基礎,相當於公路橋樑;

      Parcel 在 IBinder 基礎上傳輸數據,相當於運輸工具;

      Parcelable 基本數據類型和實現了 Parcelable 接口的複合數據類型纔可被 Parcel 傳輸,相當於擺放整齊、安檢合格的貨物;

      Stub,Stub.Proxy 實現跨進程調用的接口,相當於收發貨方。


      注:Binder,BinderProxy 都實現了 IBinder 接口


下面以 Activity 與 Service 通信爲例來分析其運行機制。

      示例目的:通過跨進程調用方式在 Activity 進程端調用 Service 進程端的方法。這些方法由接口 RemoteSSO 定義。這裏假設兩者是運行在不同進程的(也可以運行在相同進程,具體因配置而不同)。


代碼實現

1、定義 RemoteSSO.aidl 文件:

package com.sina.sso;
interface RemoteSSO {
    String getPackageName();
    String getActivityName();
}

2、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;
}

3、定義 MyRemoteSSO extends RemoteSSO.Stub。由於 RemoteSSO.Stub 是抽象類,我們應該繼承該類並實現相應的功能。這裏是以下兩方法:

public java.lang.String getPackageName() throws android.os.RemoteException;
public java.lang.String getActivityName() throws android.os.RemoteException;

4、在 Service 的 onBind() 方法裏面返回 MyRemoteSSO 對象。

5、在 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();
                            ...
                }
}

至此,通道構造完畢,可以進行跨進程通信了。


運行機制

1、綁定 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 對象的實例。


2、調用與返回

      畢竟,我們的最終目的是通信,這裏就是調用方法。那麼當我們拿到 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;
    ...
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章