Binder
是一套基于CS的架構。下面以一個極簡的例子來學習
Binder
。
1.首先定義一個 IMedia.aidl
檔案。
IMedia.aidl
interface IMedia {
boolean start();
boolean stop();
}
2.然後IDE會幫我們自動生成一個 IMedia.java
檔案
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, );
_reply.readException();
_result = ( != _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()
方法是在哪個線程被執行的
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()
開啟了循環,線程是被阻塞的。