1. 程式人生 > >Android — Activity與Service互動之Binder總結

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; }

這是格式化之後的樣子,我對這個類做了一個初步的結構圖繪製如下:
aidl結構圖1
可以比較明顯的看到類的巢狀結構,一個介面類:IController,裡面包含了介面方法(即我們宣告的那個方法)和 一個Stub類:

為遮蔽客戶呼叫遠端主機上的物件,必須提供某種方式來模擬本地物件,這種本地物件稱為存根(stub),存根負責接收本地方法呼叫,並將它們委派給各自的具體實現物件

我覺得上面這個解釋還可以,基本表達了Stub的作用~

看完上面的圖,覺得還是有點亂,既然是static class 靜態內部類,那其實我們可以把裡面的class抽離出來,作為一個新的檔案,參考下圖結構:
2
其中IController、Stub、Proxy類可以看成三個獨立的類/介面
繼承關係圖也可以很清晰的看出來,那這三者是如何相互合作使用的呢?下面這個圖可以很好的表達出具體的Activity與本地/遠端Service互動邏輯和對應上面幾個類的使用:
3

圖注:
左面的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)也是可以的~~

就先寫到這,如果有不對的地方,歡迎指正,多謝~~