android Binder queryLocalInterface 本地與遠程

在使用Binder 通信的時候有一個疑問,他是如何判斷我們是否需要跨進程也就是返回的是本地的Binder 對象還是BinderProxy。

帶着問題,我們先來看下service 的綁定過程,我們通過

ServiceConnection 接口監聽綁定情況,大致是這樣的
 bindService(new Intent(this, MyService.class), new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                IMyAidlInterface iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
            }

            @Override
            public void onServiceDisconnected(ComponentName name) {

            }
        }, BIND_AUTO_CREATE);

這裏會有兩個方法回調,onServiceConnected 中IBinder  類型數據就是關鍵,當我們綁定的是一個本地方法的時候,他返回的是一個Binder 類型的子類,這時我們可以強制類型轉換爲子類,如果是一個遠程bidner調用,則是一個BinderProxy類型,需要我們通過

IMyAidlInterface.Stub.asInterface(service);來轉爲我們定義的接口類型,那麼Binder是如何區分返回binder代理還是binder的呢,藉助我們上節aidl 定義的接口類(多進程間通信AIIDL使用)的例子
IMyAidlInterface類,首先看下mainfist 中service 的註冊
<service
            android:name=".MyService"
            android:enabled="true"
            android:exported="true"
            android:process=":remote">
            <intent-filter>
                <action android:name="com.example.service.aidl"/>
                <category android:name="android.intent.category.DEFAULT"/>
            </intent-filter>
        </service>
當 android:process=":remote" 說明該service 單獨運行在一個進程中,

當bindService 時會創建該service 的實例,這時service 會走生命週期,通過onBind 返回一個IBinder類型 接口。因爲所有的IBinder aidl 遠程調用都要繼承自IMyAidlInterface.Stub  所以必然會調用到

 public Stub()
    {
      this.attachInterface(this, DESCRIPTOR);
    }

Stub 的空構造函數。而Stub 本身又是Binder 的子類。調用了Binder 的attachInterface 方法。

  public void attachInterface(@Nullable IInterface owner, @Nullable String descriptor) {
        mOwner = owner;
        mDescriptor = descriptor;
    }

分別保存了當前類的實例對象(This)和當前類接口描述符(DESCRIPTOR),其實就是

private static final java.lang.String DESCRIPTOR = "com.example.aidlapplication.IMyAidlInterface";

當前類的全路徑,這也是我們在使用aidl 的時候服務端和客戶端必須要保證相同路徑下的原因,因爲他被保存下來作爲參數用於比對當前類是否是本地類還是遠程。

當然,如果你的service 運行在一個單獨的進程中,當你綁定成功(啓動遠程服務成功),這些數據會被保存在遠程Binder 對像中,當onServiceConnected

public void onServiceConnected(ComponentName name, IBinder service) {
    IMyAidlInterface iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service)
}

被調用,我們調用IMyAidlInterface.Stub.asInterface 來獲取一個 IMyAidlInterface 類型的類,這個Stub 是客戶端本地的Stub ,

public static com.example.aidlapplication.IMyAidlInterface asInterface(android.os.IBinder obj)
    {
      if ((obj==null)) {
        return null;
      }
      android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
      if (((iin!=null)&&(iin instanceof com.example.aidlapplication.IMyAidlInterface))) {
        return ((com.example.aidlapplication.IMyAidlInterface)iin);
      }
      return new com.example.aidlapplication.IMyAidlInterface.Stub.Proxy(obj);
    }
    @Override public android.os.IBinder asBinder()
    {
      return this;
    }

這裏首先會調用queryLocalInterface  獲取接口對象,參數 :接口描述符(DESCRIPTOR),和上邊我們在服務端的一樣,這個方法是用來查詢本地接口的,有直接返回,沒有就返回mull。當然這個obj 是binder 代理 類型是,因爲我們的服務在遠程所以返回的是一個BinderProxy ,調用的是BinderProxy 的 queryLocalInterface

  public @Nullable IInterface queryLocalInterface( String descriptor) {
       
        return null;
    }

直接返回了一個null,也就是asInterface 中iin爲null 所以返回了一個

 com.example.aidlapplication.IMyAidlInterface.Stub.Proxy(obj) 也就是  IMyAidlInterface 的代理對象,我們可以通過這個代理接口遠程調用。

當然如果是一個本地Binder,執行的是Binder 中的queryLocalInterface 方法,

 public @Nullable IInterface queryLocalInterface(@NonNull String descriptor) {
        if (mDescriptor != null && mDescriptor.equals(descriptor)) {
            return mOwner;
        }
        return null;
    }

這裏mDescriptor 就是我上邊說的本地Binder 初始化時保存的接口描述符,這裏兩個進行對比,如果一樣,就返回本地接口對象(上邊邊提到過)。

然後返回((com.example.aidlapplication.IMyAidlInterface)iin),返回本地久接口實例,這樣就不走binder跨進程通信 ,走本地通信就可以了。

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