Android 中的 Binder

Binder簡析

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

應(yīng)用層的使用 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 數(shù)據(jù)類繼承 IInterface 接口,因為可以在 Binder 中傳輸?shù)臄?shù)據(jù)接口都需要繼承 IInterface 并實現(xiàn)其 asBinder 方法

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

服務(wù)端

首先看客戶端綁定時服務(wù)端的返回的客戶端所需的 AIDL 接口類型對象,服務(wù)端返回的 Binder 對象繼承的是 AIDL 中內(nèi)部類 Stub 類,因為 Java 中多態(tài)的存在,我們需要 IBinder 對象時返回 IBinder 的子類即可。說一下為什么 服務(wù)端 返回的是 AIDL 中的內(nèi)部類 Stub 類的對象的子類,因為,該抽象類的子類實現(xiàn)了我們定義的 .aidl 接口中的方法,所以返回的應(yīng)該是這個 Stub 類的子類。Stub 子類實現(xiàn)了 .aidl 中聲明的方法的抽象方法。

服務(wù)器端 Stub 的子類實例化時,因為同時是 Binder 的子類,需要調(diào)用 Binder 的 attachInterface 方法,將本身和 IInterface 的描述字符串添加到 Binder 類中,Binder 類中會根據(jù)描述字符串匹配相應(yīng)的 IInterface 對象

客戶端

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

客戶端得到服務(wù)端返回的 Binder 后,會通過自定義 AIDL 接口類的 asInterface 將該 Binder 轉(zhuǎn)化為客戶端需要的接口類型對象,asInterface 方法會從 Binder 類中根據(jù)描述字符串尋找相應(yīng)的 IInterface,客戶端和服務(wù)端不在統(tǒng)一進程時,由于 Binder 類的加載也是分開的,所以在客戶端的 Binder 中不會找到匹配的 IInterface

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

在跨進程調(diào)用時,由于客戶端 Binder 中不能找到對應(yīng)的 IInterface,所以 asInterface 中返回的是根據(jù) Stub 的子類對象構(gòu)造的 Stub.Proxy 類的對象。Stub.Proxy 類中調(diào)用服務(wù)器端方法時會將參數(shù)和返回值都使用序列化處理,經(jīng)過跨進程通信后最終傳遞到客戶端。從而完成客戶端調(diào)研服務(wù)端服務(wù)和數(shù)據(jù)的功能。

為什么服務(wù)器端返回的 Binder 是客戶端需要的類對象,但是跨進程時需要轉(zhuǎn)換為 Proxy

因為不同進程間的對象不能相互調(diào)用,雖然綁定玩成時客戶端拿到了服務(wù)器端對象的引用但是并不能直接操作該對象,所以需要通過 Proxy 來代理,Proxy 類的方法執(zhí)行時會在 Native 層通過系統(tǒng)進程的協(xié)助調(diào)用服務(wù)端對象來執(zhí)行任務(wù),所以跨進程時客戶端拿到的 Binder 對象不可以直接強制轉(zhuǎn)型為需要的對象。

進程間通信

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

Binder 的 transact 方法中會調(diào)用 Native 層的方法,Native 層會根據(jù) IInterface 描述字符串從系統(tǒng)中所有進程的 Binder 中找到客戶端 Proxy 對應(yīng)的服務(wù)器端的 IInterface ,然后通過其 asBinder 方法得到服務(wù)器端的 Binder 對象也就是 Stub 對象,在通過調(diào)用其 onTransact 方法,通過系統(tǒng)進程將客戶端跟服務(wù)端連接,并將客戶端線程掛起

說一下這個 Binder , 創(chuàng)建 Proxy 時構(gòu)造方法傳入的 IBinder 也就是客戶端綁定時得到的 Binder 類對象,通過系統(tǒng)進程調(diào)用服務(wù)端的 onTransact 方法,執(zhí)行也就切換到了服務(wù)端,服務(wù)端也就拿到了序列化后的參數(shù),返回值以及代表哪個方法的 int 值。transact 方法中會把存儲參數(shù)和返回值的可序列化數(shù)據(jù)傳遞到 onTransact 方法中

onTransact 方法中會將序列化的參數(shù)反序列化,再根據(jù)代表客戶端調(diào)用哪個方法的 int 值,將參數(shù)傳入相應(yīng)方法得到返回值。 這里需要注意,傳入相應(yīng)的方法,其實是調(diào)用的 Stub 類的子類的方法,也就是服務(wù)端實現(xiàn)了 Stub 類中抽象方法的那個類的對象的方法,這樣最終任務(wù)的執(zhí)行實在 Stub 中執(zhí)行的,并不是在 Proxy 類中。 再將返回值進行序列化后寫入?yún)?shù)中傳來的輸出型 Parcel 中,這時候,注意,服務(wù)端就執(zhí)行結(jié)束了。

服務(wù)端執(zhí)行結(jié)束之后,系統(tǒng)進程會返回一個 Boolean 值表示是否成功調(diào)用,系統(tǒng)進程收到這個返回值時后將該返回值和及 將輸出型的 Pacel 返回到客戶端進程,客戶端進程的 Binder 線程會被喚醒,客戶端線程喚醒后,會根據(jù)是否執(zhí)行成功的返回值接收執(zhí)行結(jié)果

注:

  1. 客戶端發(fā)起請求時當前線程就會掛起,所以要是執(zhí)行任務(wù)耗時則需要客戶端在子線程中調(diào)用

  2. 服務(wù)端相應(yīng)請求執(zhí)行過程在 Binder 的線程池中,即已經(jīng)在子線程中了,所有 Binder 中執(zhí)行過程無需再開啟子線程

Binder 的死亡代理

通過 linkToDeath 和 unlinkToDeath 方法可以為 Binder 綁定和解綁死亡代理
死亡代理是一個 DeathRecipient 類,內(nèi)部又一個 binderDied 方法,我們需要實現(xiàn)這個方法,在 Binder 死亡的時候,系統(tǒng)就會回調(diào) binderDied 方法,我們就可以移除之前綁定的 binder 代理并重新綁定完成服務(wù)。

總結(jié)

  • Worker 需要傳遞的對象需要實現(xiàn)的接口,繼承 IInterface 接口

  • Stub 服務(wù)器端需要實現(xiàn)的對象的父類繼承 Binder 類

  • Proxy 代理類,客戶端調(diào)用服務(wù)器端對象時使用的類,繼承 Binder

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

AIDL原理

Binder 在 AIDL 中應(yīng)用,服務(wù)器端將客戶端需要的對象轉(zhuǎn)換成一個 Binder 對象

AIDL 原理,即客戶端需要一個服務(wù)器端的對象,客戶端和服務(wù)器端不在同一進程,服務(wù)端返回一個 Binder 對象給客戶端,客戶端根據(jù)將 Binder 對象轉(zhuǎn)換成需要的對象的 Proxy 代理,需要調(diào)用服務(wù)端執(zhí)行任務(wù)時就調(diào)用該 Proxy 代理的方法,Proxy 代理的方法執(zhí)行時會通過 Native 層通過系統(tǒng)進程找到其他進程中匹配的 Stub 子類,并調(diào)用其執(zhí)行任務(wù)方法,從而實現(xiàn)客戶端和服務(wù)端的進程間通信。

工作過程

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

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

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

  3. 接口中定義內(nèi)部類 Stub 在接口中定義 Stub 類,該類實現(xiàn)了我們需要的接口,并繼承了 Binder

  4. 服務(wù)端實現(xiàn)接口 服務(wù)端要定義類實現(xiàn) Stub 類,然后完成我們自己定義的接口的抽象方法

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

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

  7. 同進程對象工作 客戶端得到需要的對象后,調(diào)用其方法,如果是同進程,參數(shù)可以直接傳遞,同時 asInterface 得到的就是服務(wù)端實現(xiàn)的 Stub 對象,可以直接執(zhí)行方法返回結(jié)果

  8. 跨進程對象工作 如果是跨進程時,asInterface 得到的就是 Stub 的內(nèi)部類 Proxy 類的對象,該對象繼承了我們需要的接口,并實現(xiàn)了其抽象方法,Proxu 的構(gòu)造需要一個 Stub 類對象

  9. Proxy 的工作過程 其實現(xiàn)的抽象方法執(zhí)行時,會先將方法參數(shù)序列化,再調(diào)用 Stub 方法的 transact 方法將序列化后的參數(shù),方法 int 類型的標識傳入,線程掛起,服務(wù)端在 Binder 進程中執(zhí)行任務(wù)后將序列化后的結(jié)果返回。Proxy 的工作方法中,得到結(jié)果后,將序列化后的結(jié)果反序列化成方法的返回值類型,返回。

  10. transact 方法 transact 方法最后會調(diào)用 Stub 的 onTransact 方法

  11. Stub 的 onTransact 方法 onTransact 方法執(zhí)行在服務(wù)器進程的 Binder 線程池中,根據(jù)方法標識,將序列化后的參數(shù)反序列化,在服務(wù)端執(zhí)行,得到返回值后序列化,最后將結(jié)果返回,傳回到客戶端??蛻舳颂幚砗笸瓿伞?/p>

Binder 機制在系統(tǒng)中的應(yīng)用

Android 系統(tǒng)啟動之后會啟動很多的 Service,例如 ActivityManagerService 等,在開發(fā)中我們可能需要調(diào)用這些 Service 的方法,但是由于是不同進程是不可以直接調(diào)用的,這時候通過 ServiceManager 我們可以 Binder 機制跨進程拿到 AMS 的代理 ActivityManagerProxy ,通過代理即可實現(xiàn)調(diào)用 AMS 的方法。

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

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容