Binder
是一套基于CS的架構(gòu)愿汰。下面以一個極簡的例子來學習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
的接口,它的內(nèi)部類Stub
實現(xiàn)了android.os.IInterface
的asBinder()
方法,直接返回了Sub
類實例缩擂。我們繼續(xù)看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
對象轉(zhuǎn)換為方便使用的IMedia
接口對象,這個遠程Binder
對象是我們bindService()
時返回的添寺。首選通過obj.queryLocalInterface(DESCRIPTOR)
查詢是否有本地接口對象胯盯,這種情況是當Server
端和Client
端處于同一進程,沒有必要進行多進程通信计露。如果沒有本地接口對象博脑,那么直接new一個Stub.Proxy(obj)
實例返回。我們來看一下這個Proxy
類的具體實現(xiàn)
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
類的內(nèi)部類票罐,其實就是對obj
的一層封裝(代理)叉趣,那么我們通過這個代理類,就很容易和Server
端進行通信了该押。具體實現(xiàn)也很簡單疗杉,就是調(diào)用遠程Binder對象mRemote
的transact()
方法,將數(shù)據(jù)發(fā)送給Server
端蚕礼。
Stub
是一個抽象類烟具,繼承于Binder
類,實現(xiàn)了父類的onTransact()
方法奠蹬,onTransact()
方法的作用是執(zhí)行Server端的操作朝聋,并將操作的結(jié)果返回給Client端。一般情況囤躁,我們需要寫一個子類繼承于Stub
類冀痕,然后實現(xiàn)IMedia
的接口方法,這些方法會被父類onTransact()
方法調(diào)用到狸演。
3.總結(jié)一下多進程通信過程:
(1)通過bindService()
得到遠程Binder
對象obj
(2)通過IMedia.Stub.asInterface(obj)
得到一個遠程代理類Sub.Proxy
對象mediaRemoteProxy
(3)調(diào)用mediaRemoteProxy.start()
方法言蛇,事實上是調(diào)用了其內(nèi)部遠程Binder對象mRemote
的transact()
方法,將數(shù)據(jù)發(fā)送給Server
端
(4)服務(wù)端收到Client
端發(fā)送過來的數(shù)據(jù)時onTransact()
會被調(diào)用严沥,有一個子類繼承于IMedia.Stub
類猜极,實現(xiàn)了start()
方法,onTransact()
被調(diào)用時消玄,自然調(diào)用到了子類里的start()
方法跟伏。換言之,Server
端的操作被Client
調(diào)用執(zhí)行了翩瓜。
4.start()
方法是在哪個線程被執(zhí)行的
private IMedia.Stub stub = new IMedia.Stub() {
@Override
public boolean start() throws RemoteException {
Log.d("cmy", "media start:" + Thread.currentThread().getId());
return true;
}
};
通過調(diào)試程序發(fā)現(xiàn)start()
方法被執(zhí)行在名為binder1
或binder2
的線程中受扳,而不是主線程
。這也解釋了系統(tǒng)的ActivityManagerService
向應用進程發(fā)送消息時兔跌,需要使用H
類把消息轉(zhuǎn)發(fā)到UI線程
勘高,而且也必須這么做,因為UI線程
調(diào)用了Looper.loop()
開啟了循環(huán),線程是被阻塞的华望。