Android 中的 Binder 機制

Binder簡析

直觀來說,Binder 是 Android 中的一個類,是繼承了 IBinder 接口;從 IPC 角度考慮 Binder 是進程間通信的一種方式;從 Framework 層,Binder 是 連接 ServiceManager 和 各種 Manager(AM,WM) 以及各種 ManagerService 的橋樑;從應用層來說,Binder 是客戶端和服務器端進行通信的媒介,當 bindService 的時候服務器端會返回一個包含了服務端業務調用的 Binder 對象,客戶端就可以獲取服務端提供的數據或服務。

應用層的使用 AIDL

/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: D:\\MyApplication\\ServiceDemo\\app\\src\\main\\aidl\\com\\renxl\\servicedemo\\aidl\\MyWorker.aidl
 */
package com.renxl.servicedemo.aidl;
// Declare any non-default types here with import statements

public interface MyWorker extends android.os.IInterface {
    /**
     * Local-side IPC implementation stub class.
     */
    public static abstract class Stub extends android.os.Binder implements com.renxl.servicedemo.aidl.MyWorker {
        private static final java.lang.String DESCRIPTOR = "com.renxl.servicedemo.aidl.MyWorker";

        /**
         * Construct the stub at attach it to the interface.
         */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        /**
         * Cast an IBinder object into an com.renxl.servicedemo.aidl.MyWorker interface,
         * generating a proxy if needed.
         */
        public static com.renxl.servicedemo.aidl.MyWorker asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.renxl.servicedemo.aidl.MyWorker))) {
                return ((com.renxl.servicedemo.aidl.MyWorker) iin);
            }
            return new com.renxl.servicedemo.aidl.MyWorker.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_doWork: {
                    data.enforceInterface(DESCRIPTOR);
                    java.lang.String _arg0;
                    _arg0 = data.readString();
                    int _result = this.doWork(_arg0);
                    reply.writeNoException();
                    reply.writeInt(_result);
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements com.renxl.servicedemo.aidl.MyWorker {
            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 int doWork(java.lang.String str) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                int _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeString(str);
                    mRemote.transact(Stub.TRANSACTION_doWork, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readInt();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }

        static final int TRANSACTION_doWork = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    }

    public int doWork(java.lang.String str) throws android.os.RemoteException;
}

介紹

我們自定義的 AIDL 數據類繼承 IInterface 接口,因爲可以在 Binder 中傳輸的數據接口都需要繼承 IInterface 並實現其 asBinder 方法

Stub 是 AIDL 接口中的抽象類,Stub 類繼承了 Binder 和我們需要在進程中傳遞的數據類型接口,Stub 類中還有 Proxy 子類,該類也繼承了需要在進程間傳遞的數據類型

服務端

首先看客戶端綁定時服務端的返回的客戶端所需的 AIDL 接口類型對象,服務端返回的 Binder 對象繼承的是 AIDL 中內部類 Stub 類,因爲 Java 中多態的存在,我們需要 IBinder 對象時返回 IBinder 的子類即可。說一下爲什麼 服務端 返回的是 AIDL 中的內部類 Stub 類的對象的子類,因爲,該抽象類的子類實現了我們定義的 .aidl 接口中的方法,所以返回的應該是這個 Stub 類的子類。Stub 子類實現了 .aidl 中聲明的方法的抽象方法。

服務器端 Stub 的子類實例化時,因爲同時是 Binder 的子類,需要調用 Binder 的 attachInterface 方法,將本身和 IInterface 的描述字符串添加到 Binder 類中,Binder 類中會根據描述字符串匹配相應的 IInterface 對象

客戶端

根據綁定模式的 Service 綁定過程,最終服務端返回的 Binder 對象會傳遞到客戶端的 ServiceConnection 的 onServiceConnected 方法中。

客戶端得到服務端返回的 Binder 後,會通過自定義 AIDL 接口類的 asInterface 將該 Binder 轉化爲客戶端需要的接口類型對象,asInterface 方法會從 Binder 類中根據描述字符串尋找相應的 IInterface,客戶端和服務端不在統一進程時,由於 Binder 類的加載也是分開的,所以在客戶端的 Binder 中不會找到匹配的 IInterface

在同進程請求時 asInterface 返回的是 Stub 的實現類的對象,也就是直接返回的服務端返回的繼承了 Stub 類的對象。同進程不用考慮 Binder 傳輸數據,所以直接調用 Stub 實現類的各種方法即可實現客戶端調用服務端的數據和服務的功能。

在跨進程調用時,由於客戶端 Binder 中不能找到對應的 IInterface,所以 asInterface 中返回的是根據 Stub 的子類對象構造的 Stub.Proxy 類的對象。Stub.Proxy 類中調用服務器端方法時會將參數和返回值都使用序列化處理,經過跨進程通信後最終傳遞到客戶端。從而完成客戶端調研服務端服務和數據的功能。

爲什麼服務器端返回的 Binder 是客戶端需要的類對象,但是跨進程時需要轉換爲 Proxy

因爲不同進程間的對象不能相互調用,雖然綁定玩成時客戶端拿到了服務器端對象的引用但是並不能直接操作該對象,所以需要通過 Proxy 來代理,Proxy 類的方法執行時會在 Native 層通過系統進程的協助調用服務端對象來執行任務,所以跨進程時客戶端拿到的 Binder 對象不可以直接強制轉型爲需要的對象。

進程間通信

跨進程調用時客戶端的調用邏輯:上面說了跨進程時得到的AIDL接口數據類型是 Stub.Proxy ,所以直接分析 Stub.Proxy 類中的方法調用。Stub.Proxy 中調用方法並不會直接調用服務端的方法,會先創建 Parcel 類型的輸入對象和輸出對象以及返回值。如果方法有參數就將參數寫入輸入型 Parcel 對象中,這個過程還是在客戶端,接下來會將序列化後的參數,序列化的返回值,以及表示客戶端調用的是哪個方法的 int 值傳入 Binder 的 transact 方法,客戶端線程掛起。

Binder 的 transact 方法中會調用 Native 層的方法,Native 層會根據 IInterface 描述字符串從系統中所有進程的 Binder 中找到客戶端 Proxy 對應的服務器端的 IInterface ,然後通過其 asBinder 方法得到服務器端的 Binder 對象也就是 Stub 對象,在通過調用其 onTransact 方法,通過系統進程將客戶端跟服務端連接,並將客戶端線程掛起

說一下這個 Binder , 創建 Proxy 時構造方法傳入的 IBinder 也就是客戶端綁定時得到的 Binder 類對象,通過系統進程調用服務端的 onTransact 方法,執行也就切換到了服務端,服務端也就拿到了序列化後的參數,返回值以及代表哪個方法的 int 值。transact 方法中會把存儲參數和返回值的可序列化數據傳遞到 onTransact 方法中

onTransact 方法中會將序列化的參數反序列化,再根據代表客戶端調用哪個方法的 int 值,將參數傳入相應方法得到返回值。 這裏需要注意,傳入相應的方法,其實是調用的 Stub 類的子類的方法,也就是服務端實現了 Stub 類中抽象方法的那個類的對象的方法,這樣最終任務的執行實在 Stub 中執行的,並不是在 Proxy 類中。 再將返回值進行序列化後寫入參數中傳來的輸出型 Parcel 中,這時候,注意,服務端就執行結束了。

服務端執行結束之後,系統進程會返回一個 Boolean 值表示是否成功調用,系統進程收到這個返回值時後將該返回值和及 將輸出型的 Pacel 返回到客戶端進程,客戶端進程的 Binder 線程會被喚醒,客戶端線程喚醒後,會根據是否執行成功的返回值接收執行結果

注:
1. 客戶端發起請求時當前線程就會掛起,所以要是執行任務耗時則需要客戶端在子線程中調用

  1. 服務端相應請求執行過程在 Binder 的線程池中,即已經在子線程中了,所有 Binder 中執行過程無需再開啓子線程

Binder 的死亡代理

通過 linkToDeath 和 unlinkToDeath 方法可以爲 Binder 綁定和解綁死亡代理
死亡代理是一個 DeathRecipient 類,內部又一個 binderDied 方法,我們需要實現這個方法,在 Binder 死亡的時候,系統就會回調 binderDied 方法,我們就可以移除之前綁定的 binder 代理並重新綁定完成服務。

總結

  • Worker 需要傳遞的對象需要實現的接口,繼承 IInterface 接口

  • Stub 服務器端需要實現的對象的父類繼承 Binder 類

  • Proxy 代理類,客戶端調用服務器端對象時使用的類,繼承 Binder

當 Stub 的子類實例化時,Binder 中會存起來,並將該 Binder 返回到客戶端,客戶端拿到這個 Binder 會根據是否是同進程操作,不同進程時會通過 Binder 構造

AIDL原理

Binder 在 AIDL 中應用,服務器端將客戶端需要的對象轉換成一個 Binder 對象

AIDL 原理,即客戶端需要一個服務器端的對象,客戶端和服務器端不在同一進程,服務端返回一個 Binder 對象給客戶端,客戶端根據將 Binder 對象轉換成需要的對象的 Proxy 代理,需要調用服務端執行任務時就調用該 Proxy 代理的方法,Proxy 代理的方法執行時會通過 Native 層通過系統進程找到其他進程中匹配的 Stub 子類,並調用其執行任務方法,從而實現客戶端和服務端的進程間通信。

工作過程

首先,我們服務器端有一個可以執行任務的對象需要傳遞到客戶端,然而不同進程之間不可以直接調用對象的方法,從而通過 Binder 來實現進程間的通信。

  1. aidl文件 定義 .aidl 類型文件,其中聲明要實現的功能方法

  2. 定義接口 首先,定義 AIDL 類型的接口,也就是需要傳遞的對象類型接口,繼承 IInterface ,並在其中根據 aidl 文件的內容定義了這個類的可以實現功能的抽象方法

  3. 接口中定義內部類 Stub 在接口中定義 Stub 類,該類實現了我們需要的接口,並繼承了 Binder

  4. 服務端實現接口 服務端要定義類實現 Stub 類,然後完成我們自己定義的接口的抽象方法

  5. Stub 類的實現 Stub 類中定義了 asInterface 可以返回一個我們需要的類對象,如果是同進程則返回本身,如果是非同進程則返回 Stub 的內部類 Proxy 代理類對象

  6. 客戶端請求 客戶端綁定時服務器返回服務端實現的接口類型的對象,客戶端得到的 Binder 對象就是 Stub 對象,客戶端調用 Stub 對象的 asInterface 方法得到需要的類型對象

  7. 同進程對象工作 客戶端得到需要的對象後,調用其方法,如果是同進程,參數可以直接傳遞,同時 asInterface 得到的就是服務端實現的 Stub 對象,可以直接執行方法返回結果

  8. 跨進程對象工作 如果是跨進程時,asInterface 得到的就是 Stub 的內部類 Proxy 類的對象,該對象繼承了我們需要的接口,並實現了其抽象方法,Proxu 的構造需要一個 Stub 類對象

  9. Proxy 的工作過程 其實現的抽象方法執行時,會先將方法參數序列化,再調用 Stub 方法的 transact 方法將序列化後的參數,方法 int 類型的標識傳入,線程掛起,服務端在 Binder 進程中執行任務後將序列化後的結果返回。Proxy 的工作方法中,得到結果後,將序列化後的結果反序列化成方法的返回值類型,返回。

  10. transact 方法 transact 方法最後會調用 Stub 的 onTransact 方法

  11. Stub 的 onTransact 方法 onTransact 方法執行在服務器進程的 Binder 線程池中,根據方法標識,將序列化後的參數反序列化,在服務端執行,得到返回值後序列化,最後將結果返回,傳回到客戶端。客戶端處理後完成。

Binder 機制在系統中的應用

Android 系統啓動之後會啓動很多的 Service,例如 ActivityManagerService 等,在開發中我們可能需要調用這些 Service 的方法,但是由於是不同進程是不可以直接調用的,這時候通過 ServiceManager 我們可以 Binder 機制跨進程拿到 AMS 的代理 ActivityManagerProxy ,通過代理即可實現調用 AMS 的方法。

上面提到了 ServiceManager,那 ServiceManager 又是怎麼工作的呢,ServiceManager 其實是 ServiceManagerProxy 的代理,ServiceManagerProxy 是 ServiceManagerNative 的代理,ServiceManagerNative 繼承了 IServiceManager ,我們看出來了,原來 ServiceManager 也是通過 Binder 實現跨進程的

我們在應用中使用 ServiceManager 在獲取系統服務時,如果 ServiceManager 代理的 ServiceManagerNative 沒有初始化,則會通過 Natice 層來通過 Binder 機制完成 ServiceManagerNative 初始化,此時的服務端是系統進程,之後我們就可以使用 ServiceManager 來協助我們獲取其他的系統服務了。

發佈了28 篇原創文章 · 獲贊 9 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章