Android — Activity與Service互動之Binder總結
最近在複習Android跨程序呼叫,整理了一下自己對Binder的一些理解,希望能對大家也有所幫助,如有錯誤歡迎指正~
為了加深理解,希望看完後能自己操練驗證一下,要不真的很容易眼高手低哦
ps:不想細看,可以只看裡面的3張圖片即可~ ^_^
好了,迴歸主題,我們現在寫個AIDL檔案:
#IController.aidl
// Declare any non-default types here with import statements
interface IController {
void showMessage(String msg);
}
我們先來看一下預設生成檔案的註釋:
意思是說:如果不是預設型別,需要進行相應的import,類似java導包一樣
那預設型別有哪些呢?
int、long、boolean、float、double、String、in/out/inout List
而Parcelable的實現類、aidl介面類
這個是需要進行import的
新增完AIDL檔案後,我們編譯一下,然後找到IController.java類,這個類是系統幫我們把AIDL檔案轉化為一個Java類,便於我們進行使用,當然我們自己也可以寫~~
我們來看一下這個類的程式碼:
public interface IController extends android .os.IInterface {
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements com.example.yukai.test.IController {
private static final java.lang.String DESCRIPTOR = "com.example.yukai.test.IController";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.example.yukai.test.IController interface,
* generating a proxy if needed.
*/
public static com.example.yukai.test.IController asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.example.yukai.test.IController))) {
return ((com.example.yukai.test.IController) iin);
}
return new com.example.yukai.test.IController.Stub.Proxy(obj);
}
@Override
public android.os.IBinder asBinder() {
return this;
}
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_showMessage: {
data.enforceInterface(DESCRIPTOR);
java.lang.String _arg0;
_arg0 = data.readString();
this.showMessage(_arg0);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.example.yukai.test.IController {
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 showMessage(java.lang.String msg) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(msg);
mRemote.transact(Stub.TRANSACTION_showMessage, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
}
static final int TRANSACTION_showMessage = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
public void showMessage(java.lang.String msg) throws android.os.RemoteException;
}
這是格式化之後的樣子,我對這個類做了一個初步的結構圖繪製如下:
可以比較明顯的看到類的巢狀結構,一個介面類:IController,裡面包含了介面方法(即我們宣告的那個方法)和 一個Stub類:
為遮蔽客戶呼叫遠端主機上的物件,必須提供某種方式來模擬本地物件,這種本地物件稱為存根(stub),存根負責接收本地方法呼叫,並將它們委派給各自的具體實現物件
我覺得上面這個解釋還可以,基本表達了Stub的作用~
看完上面的圖,覺得還是有點亂,既然是static class 靜態內部類,那其實我們可以把裡面的class抽離出來,作為一個新的檔案,參考下圖結構:
其中IController、Stub、Proxy類可以看成三個獨立的類/介面
繼承關係圖也可以很清晰的看出來,那這三者是如何相互合作使用的呢?下面這個圖可以很好的表達出具體的Activity與本地/遠端Service互動邏輯和對應上面幾個類的使用:
圖注:
左面的Service是跟Activity處於同一個程序下的
右面的Service是跟Activity非同一個程序下的,可能是remote或其他
“所以測試的時候需要分別測這兩種情況,有process:remote和什麼都不寫預設程序的。”
1.與當前程序的Service互動
在bind Service後,onServiceConnected會返回對應的binder物件,我們可以除錯發現這個物件跟Service裡面的binder物件其實是 同一個物件,因此我們可以直接轉成我們Service的Binder,然後直接呼叫;當然如果用的是AIDL的方式,我們可以呼叫Stub的靜態方法:asInterface,可以做相應的轉化。
說到這裡,我們還需要看一下這個asInterface方法實現,先只看一半:
public static com.test.IController asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.test.IController))) {
return ((com.test.IController) iin);
}
--------other return-------
}
關鍵在這裡:
queryLocalInterface,檢視原始碼可以發現,裡面判斷的是引數的IBinder物件的描述符是否是DESCRIPTOR,如果是,就會返回IController這個owner物件,所以就可以進行相應的強轉並返回。
那remote返回什麼呢?那看一下第二條吧~
2.與remote程序的Service互動
我們來看一下上面省略的那行程式碼:
return new com.test.IController.Stub.Proxy(obj);
看到這裡原來返回的是個Proxy物件,那個obj會是什麼呢?通過除錯發現類名是BinderProxy物件,這個類我們的SDK裡看不到,但是我們能在原始碼裡找到,發現這個類是也是繼承IBinder類,這個類負責與底層binder進行傳遞資料,然後會呼叫遠端的Stub.onTransact,進行真實的Service方法呼叫。參照圖片可以更好的理解一下~
3. 對應的Service與Activity進行互動
我們可以通過RemoteCallbackList這個類進行相關注冊並進行回撥,這個實現起來像是Activity是服務端,Service是客戶端,大家可以實現一下看看~ 發現Stub是在Activity裡新建並傳遞給Service端,由Service進行相應回撥。當然還可以通過其他方式,比如廣播(LocalBroadcast)也是可以的~~
就先寫到這,如果有不對的地方,歡迎指正,多謝~~