关于利用AIDL编程实现进程间通信的例子可以看这篇文章Android进程间通信——AIDL Service
本文主要梳理一下Binder通信的流程,方便理解Binder机制。
还是根据上面这篇文章中的例子来说。
首先看一张图。
这张图描述的是Binder通信过程中对象的演变过程。
Remote Service通过IBinder onBind(Intent intent)
方法返回一个IBinder接口对象给Client,Client在
void onServiceConnected(ComponentName name, IBinder service)
方法中获得这个IBinder接口对象。
这是应用程序开发层面看到的Binder通信过程,画了虚线表示这一过程依赖底层实现。
右侧的流程图则更加详细地描述了这一通信过程:
1)在Remote Service端,定义一个TimeBinder实例。TimeBinder继承自ITime.Stub类,而ITime.Stub类继承自Binder同时实现了ITime接口(关于这些类之间的继承关系后面会说)。这个TimeBinder类就是服务端要返回给客户端的东西。
2)TimeBinder被包装成Parcel对象在底层传输(Binder通信在底层依赖Binder驱动);当客户端收到传过来的Parcel对象后将其解包,恢复成IBinder对象。为什么恢复成IBinder对象而不是TimeBinder对象呢?如果熟悉Java泛型的话,就会知道,泛型编程会丢失对象原来的类型信息。在Parcel中处理的都是IBinder类型的对象,所以从Parcel中读出来的也是IBinder类型的对象。
3)调用ITime.Stub的静态方法asInterface(android.os.IBinder obj)
,将IBinder对象转换成ITime接口对象。asInterface方法原型为
public static ITime asInterface(android.os.IBinder obj)
4)通过转换得到的ITime接口对象,就可以调用ITime接口中定义的方法了。这样就完成了一次远程调用。
以上是一个粗略的流程,下面结合代码来细看一下。
首先给出各个类和接口之间的关系。
结合Android进程间通信——AIDL Service一文中提到的AIDL编程的步骤,我们来看一下。
TimeBinder是在Service中定义的,它继承了ITime.Stub类
public class TimeBinder extends Stub
{
@Override
public String getCurrentDate() throws RemoteException {
// 回调方法,返回当前日期
SimpleDateFormat format=new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
String date=format.format(new Date());
return date;
}
@Override
public long getCurrentTime() throws RemoteException {
// 回调方法,返回当前毫秒
return System.currentTimeMillis();
}
}
在这个类中具体实现了ITime接口定义的方法,因此它是服务端返回给客户端的对象。
客户端在onServiceConnected方法中获得服务端传过来的IBinder对象
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// 绑定远程service成功后,回调此方法
// 通过ITime.Stub类的asInterface方法可以将该代理转换为ITime接口的对象
binder=ITime.Stub.asInterface(service);
}
在这儿客户端调用了ITime.Stub.asInterface静态方法将IBinder对象转换成ITime接口对象。ITime.Stub.asInterface方法实现如下:
public static com.example.customview.ITime asInterface(
android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
// 先查找本地有没有这个接口的对象
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
// 如果本地有这个接口的对象
// 并且这个对象实现了ITime接口
// 则将其转换成ITime接口对象
if (((iin != null) && (iin instanceof com.example.customview.ITime))) {
return ((com.example.customview.ITime) iin);
}
// 本地没有这个接口对象,就创建一个代理对象
return new com.example.customview.ITime.Stub.Proxy(obj);
}
在这个方法里主要做了两件事:
1)调用queryLocalInterface在本地查找DESCRIPTOR描述的接口的对象,找到了就返回
2)本地没有找到,则创建一个代理对象并返回
queryLocalInterface方法是在IBinder接口中定义的,在Binder类中予以实现:
public IInterface queryLocalInterface(String descriptor) {
if (mDescriptor.equals(descriptor)) {
return mOwner;
}
return null;
}
ITime.Stub类和TimeBinder类也继承了这一方法。
从类图中可以看到,每个Binder类中都有一个IInterface接口对象mOwner,这个就是本地的接口对象。
因为服务端返回的是TimeBinder,它是Binder的子类,所以它的内部也保存了这么一个mOwner。
queryLocalInterface方法中先判断需要查找的接口的descriptor是否和本地接口对象的mDescriptor相等,如果相等,就返回本地接口对象,否则返回null。
Binder里的mOwner是在哪里赋值的呢?
是在attachInterface方法中。看看Binder中对这个方法的实现:
public void attachInterface(IInterface owner, String descriptor) {
mOwner = owner;
mDescriptor = descriptor;
}
而attachInterface方法是在ITime.Stub方法的构造方法中调用的
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
TimeBinder是Stub的子类,那么构造TimeBinder实例的时候就会调用attachInterface方法,给TimeBinder内部的mOwner赋值。
所以这个mOwner在客户端连接之前就已经被赋值了。
继续看queryLocalInterface方法。
如果descriptor不等于mDescriptor,那么返回null。
asInterface方法中继续判断queryLocalInterface的返回值是否是null。
如果返回值不是null,而且返回的对象实现了ITime接口,则将其转换成ITime接口对象并返回;如果返回值是null,则创建一个Proxy对象返回。Proxy也是ITime接口的实现类,就是所谓的代理。
所以说asInterface方法的目的就是想方设法地从IBinder接口对象中得到ITime接口对象。因为得到了ITime接口对象之后就可以调用接口中定义的方法啦。
不过这儿要分两种情况来看:
第一种情况:
// 如果本地有这个接口的对象
// 并且这个对象实现了ITime接口
// 则将其转换成ITime接口对象
if (((iin != null) && (iin instanceof com.example.customview.ITime))) {
return ((com.example.customview.ITime) iin);
即本地就有ITime接口的实现对象,那么直接调用其方法即可。
第二种情况:
// 本地没有这个接口对象,就创建一个代理对象
return new com.example.customview.ITime.Stub.Proxy(obj);
即得到的是一个代理对象,那么调用其方法的时候实际上是调用了该代理对象内部IBinder对象mRemote的transact方法:
@Override public long getCurrentTime() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
long _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getCurrentTime, _data, _reply, 0);
_reply.readException();
_result = _reply.readLong();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
总结:
本文只是简单梳理了一下Binder通信的流程和各个类之间的关系,并不涉及系统底层代码。
再画一张方法调用流程图作为结束: