Android插件化基础--Binder机制
Binder
是一套基于CS的架构。下面以一个极简的例子来学习Binder
。
1.首先定义一个IMedia.aidl
文件。
interface IMedia {
boolean start();
boolean stop();
}
2.然后IDE会帮我们自动生成一个IMedia.java
文件
public interface IMedia extends android.os.IInterface {
public static abstract class Stub extends android.os.Binder implements com.cmeiyuan.pluginstudy.IMedia {
public android.os.IBinder asBinder(){
return this;
}
public static com.cmeiyuan.pluginstudy.IMedia asInterface(android.os.IBinder obj) {
...
}
public boolean onTransact(...){
...
start();
...
}
private static class Proxy implements com.cmeiyuan.pluginstudy.IMedia {
...
}
}
}
public boolean start() throws android.os.RemoteException;
}
IMedia
是一个继承于android.os.IInterface
的接口,它的内部类Stub
实现了android.os.IInterface
的asBinder()
方法,直接返回了Sub
类实例。我们继续看Sub
类的asInterface()
方法
public static com.cmeiyuan.pluginstudy.IMedia asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.cmeiyuan.pluginstudy.IMedia))) {
return ((com.cmeiyuan.pluginstudy.IMedia) iin);
}
return new com.cmeiyuan.pluginstudy.IMedia.Stub.Proxy(obj);
}
这个方法的作用是将远程Binder
对象转换为方便使用的IMedia
接口对象,这个远程Binder
对象是我们bindService()
时返回的。首选通过obj.queryLocalInterface(DESCRIPTOR)
查询是否有本地接口对象,这种情况是当Server
端和Client
端处于同一进程,没有必要进行多进程通信。如果没有本地接口对象,那么直接new一个Stub.Proxy(obj)
实例返回。我们来看一下这个Proxy
类的具体实现
private static class Proxy implements com.cmeiyuan.pluginstudy.IMedia {
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
@Override
public boolean start() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
boolean _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_start, _data, _reply, 0);
_reply.readException();
_result = (0 != _reply.readInt());
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
这个Proxy
类是Stub
类的内部类,其实就是对obj
的一层封装(代理),那么我们通过这个代理类,就很容易和Server
端进行通信了。具体实现也很简单,就是调用远程Binder对象mRemote
的transact()
方法,将数据发送给Server
端。
Stub
是一个抽象类,继承于Binder
类,实现了父类的onTransact()
方法,onTransact()
方法的作用是执行Server端的操作,并将操作的结果返回给Client端。一般情况,我们需要写一个子类继承于Stub
类,然后实现IMedia
的接口方法,这些方法会被父类onTransact()
方法调用到。
3.总结一下多进程通信过程:
(1)通过bindService()
得到远程Binder
对象obj
(2)通过IMedia.Stub.asInterface(obj)
得到一个远程代理类Sub.Proxy
对象mediaRemoteProxy
(3)调用mediaRemoteProxy.start()
方法,事实上是调用了其内部远程Binder对象mRemote
的transact()
方法,将数据发送给Server
端
(4)服务端收到Client
端发送过来的数据时onTransact()
会被调用,有一个子类继承于IMedia.Stub
类,实现了start()
方法,onTransact()
被调用时,自然调用到了子类里的start()
方法。换言之,Server
端的操作被Client
调用执行了。
4.start()
方法是在哪个线程被执行的
private IMedia.Stub stub = new IMedia.Stub() {
@Override
public boolean start() throws RemoteException {
Log.d("cmy", "media start:" + Thread.currentThread().getId());
return true;
}
};
通过调试程序发现start()
方法被执行在名为binder1
或binder2
的线程中,而不是主线程
。这也解释了系统的ActivityManagerService
向应用进程发送消息时,需要使用H
类把消息转发到UI线程
,而且也必须这么做,因为UI线程
调用了Looper.loop()
开启了循环,线程是被阻塞的。