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