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跨进程通信 ,走本地通信就可以了。

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