这篇文章,是建立在大家对binder已经有了一定认识的基数上,因此对于一些具体概念不再衍述!
对于binder,主要可以分为实名binder和匿名binder,那么何为实名,何为匿名?
1.实名binder
其实实名binder大家应该都很熟悉,每当我们需要和一个系统服务交互的时候其实就会利用实名binder和系统服务建立IPC通信。
比如我们需要获取windowmanager的服务,我们可以直接通过下面代码获取:
WindowManager wm = context.getSystemService(WINDOW_SERVICE);
其实这里我们通过代码获取的就是WMS的实名binder的proxy代理,那么这个代理又是如何返回给我们的?
我们先抛开这个不谈,我们先来整理下关于Binder使用到的元素:
IBinder
Binder implements IBinder
Stub extends android.os.Binder
Proxy implements SelfInterface
IInterface
SelfInterface extends android.os.IInterface
现在,我们先不考虑Binder驱动中的工作和Binder的注册,单从上面的元素调用来分析C/S的IPC通信:
Client端
无论是何种通信,首先都要约定通信的内容,因此我们先定义一个接口来约定通信内容:
public interface SelfInterface{
void callback(Object obj);
}
这里我们约定了一个带参数的回调方法callback(Object obj)
来和Server端进行通信。
约定了通信内容之后,我们需要一个载体来进行通信操作,就好比你寄快递,把要寄的东西打包好之后,需要填写一个快递单,并先把包裹存放在快递的寄件代理处,等快递小哥来接收,在这里,大家可以把BinderProxy看作是寄件代理处,而把IBinder看作是快递小哥:
1.收揽快递(Proxy -> IBinder):
我们进行进程间通信之前,首先需要一个IBinder的代理,它可以告诉Binder驱动把邮件寄给哪个Server端,并告诉Server端需要做些什么,Proxy就好比是存放在代理寄件处的包裹,上面标注了需要取件的快递小哥(mRemotes), 以及里面装载了邮寄的物件(实现的SelfInterface接口),当我们调用了Proxy的复写的SelfInterface中的方法后,就会通知快递小哥把包裹送往邮局(Binder Driver)。
private static class Proxy implements SelfInterface {
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 void callback(Object obj) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int[] _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_loginTFCard, _data, _reply, 0);
_reply.readException();
_result = _reply.createIntArray();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
在Proxy
中,我们通过构造函数会传入远端注册的IBinder mRemotes
,我们可以通过Stub.asInterface()
去获取对应IBinder的代理,我们当然也可以在代理中通过asBinder()
来获取这个IBinder。
我们重点来分析以下在Proxy
中复写的来自于SelfInterface
接口定义的方法。
@Override
public void callback(Object obj) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int[] _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_loginTFCard, _data, _reply, 0);
_reply.readException();
_result = _reply.createIntArray();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
这里首先会对我们的数据进行序列化,这一块涉及到数据的传输,我们不过多叙述,主要关注mRemote.transact(Stub.TRANSACTION_loginTFCard, _data, _reply, 0)
,在这里调用了IBinder
中的transact(args..)
方法来把通信内容交由Binder驱动处理,在这个过程中会阻塞下一步的执行,只有等Binder.onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags)
方法相应并且处理了客户端传来的通信内容并成功写入到reply
后,才会继续执行下面的代码来获取服务端处理的结果:
_reply.readException();
_result = _reply.createIntArray();
由此可见,Proxy在整个Binder IPC通信过程中相当于客户端寄存中心(代理中心),它会负责实现客户端想要实现的方法,并且利用通过Stub.asInterface(android.os.IBinder obj)
中缓存的IBinder
来和服务端进行Binder通信,代码中注释描述了asInterface
的功能:
public static jifuliya.lyl.retrofitproxy.IEcmServiceBinder asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
//查询IBinder是否是本地进程所注册,即IBinder是否属于自己
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof jifuliya.lyl.retrofitproxy.IEcmServiceBinder))) {
//如果属于自己则直接返回,属于进程内部通信
return ((jifuliya.lyl.retrofitproxy.IEcmServiceBinder) iin);
}
//如果不属于自己则生成对应的代理,将远程IBinder缓存到Proxy代理中
return new jifuliya.lyl.retrofitproxy.IEcmServiceBinder.Stub.Proxy(obj);
}
2&3:在邮局中的那些事
通过底层BpBinder和BbBinder的通信来完成底层的数据传递,这个后面我们单独提取出来细细道来。
4:把包裹送到目的地
远端Server会持有一个Stub,它继承了Binder,我们可以通过复写onTransact()
来和Binder驱动定义响应协议,Binder驱动将协议通知给BbBinder,在响应客户端的过程中按照定义的协议进行响应。这就好比我们在接收快递的时候可以选择代收或者亲自接收等,当完成了接收之后,我们会向邮局返回一个已经签收成功的订单回执,来告诉邮局我已经成功接受了包裹,并且可以附带一些信息给寄送快递的人,这个回执就是_reply
,相当于Server端对处理结果的反馈。
我们来看看这个Stub
到底长什么样子?
public static abstract class Stub extends android.os.Binder implements jifuliya.lyl.retrofitproxy.IEcmServiceBinder {
private static final java.lang.String DESCRIPTOR = "jifuliya.lyl.retrofitproxy.IEcmServiceBinder";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an jifuliya.lyl.retrofitproxy.IEcmServiceBinder interface,
* generating a proxy if needed.
*/
public static jifuliya.lyl.retrofitproxy.IEcmServiceBinder asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof jifuliya.lyl.retrofitproxy.IEcmServiceBinder))) {
return ((jifuliya.lyl.retrofitproxy.IEcmServiceBinder) iin);
}
return new jifuliya.lyl.retrofitproxy.IEcmServiceBinder.Stub.Proxy(obj);
}
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
java.lang.String descriptor = DESCRIPTOR;
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(descriptor);
return true;
}
case TRANSACTION_callback: {
data.enforceInterface(descriptor);
java.lang.String _arg0;
_arg0 = data.readString();
int[] _result = this.loginTFCard(_arg0);
reply.writeNoException();
reply.writeIntArray(_result);
return true;
}
default: {
return super.onTransact(code, data, reply, flags);
}
}
}
}
这里面的DESCRIPTOR
相当于一个身份name,作为身份凭证,asInterface()
在#1中我们已经分析过了,主要是为了生成改Binder的一个代理,作为客户端的通信入口,现在我们把重点放在onTransact()
上:
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
java.lang.String descriptor = DESCRIPTOR;
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(descriptor);
return true;
}
case TRANSACTION_callback: {
data.enforceInterface(descriptor);
java.lang.String _arg0;
_arg0 = data.readString();
//this.loginTFCard(_arg0)为SelfInterface接口中的方法在服务端的具体实现
int[] _result = this.loginTFCard(_arg0);
reply.writeNoException();
reply.writeIntArray(_result);
return true;
}
default: {
return super.onTransact(code, data, reply, flags);
}
}
}
我们所有在客户端中定义的每一个通信内容,都会生成一个类似于TRANSACTION_callback
的code,彼此之间不会重复,当我们通过Binder驱动在onTransact()
拿到data后,会将data中的参数通过data.readString()
读取出来,并将参数传入到自己对应的callback(param)
中按照自己约定的逻辑协议处理数据,最后将结果通过reply.writeIntArray(_result)
写到序列化的reply
中。
5.收件回执reply
我们通过一张图来表述reply
在整个Binder通信中的生命周期:
这里注意一点,transact()
中执行到mRemote.transact()
后会等待onTransact()
成功把result
写入reply
并回传后才会继续下面的代码指令执行。
当1-5执行完毕后,一次进程间的Binder IPC通信就算完成了,是不是很简单?
下一章我将带大家在此基础上详细分析实名Binder的注册和通信。