Binder 上層原理淺析

Binder上層原理淺析

1. IBinder簡介

Binder實現IBinder接口,IBinder是一個接口,代表一種跨進程傳輸的能力,實現此接口,就可以將此對象進行跨進程傳遞。

根據IBinder的源碼介紹及分析,可以瞭解很多信息(譯自IBinder的源碼介紹並總結):

1. IBinder是高性能、輕量級進程間通信的基本接口,該接口定義了與遠程對象通信的協議。不能直接實現此接口,而要繼承自它的實現類Binder

2. IBinder主要API是transact()和Binder的onTransact(), 這兩個方法進行發送和接收Binder對象,調用trasact的返回數據會在onTransact回調後返回。

場景:調用IBinder的transact()給IBinder對象發送請求,然後經過Binder的Binder.onTransact()得到調用,遠程操作的目標得到回調。

IBinder的transact方法:

    /**
     * Perform a generic operation with the object.
     * 
     * @param code The action to perform.  This should
     * be a number between {@link #FIRST_CALL_TRANSACTION} and
     * {@link #LAST_CALL_TRANSACTION}.
     * @param data Marshalled data to send to the target.  Must not be null.
     * If you are not sending any data, you must create an empty Parcel
     * that is given here.
     * @param reply Marshalled data to be received from the target.  May be
     * null if you are not interested in the return value.
     * @param flags Additional operation flags.  Either 0 for a normal
     * RPC, or {@link #FLAG_ONEWAY} for a one-way RPC.
     */

    public boolean transact(int code, Parcel data, Parcel reply, int flags)
        throws RemoteException;

與他對應的Binder的onTransact方法:

    /**
     * Default implementation is a stub that returns false.  You will want
     * to override this to do the appropriate unmarshalling of transactions.
     *
     * <p>If you want to call this, call transact().
     */

    protected boolean onTransact(int code, Parcel data, Parcel reply,
            int flags) throws RemoteException {}

兩個方法的參數一致,參數具體意義是什麼?

  • code:要執行的動作,類似於Handler的what,自定義Code值需要在IBinder.FIRST_CALL_TRANSACTION(0x00000001)和IBinder.LAST_CALL_TRANSACTION (0x00ffffff)之間。其中IBinder內部定義了部分code:
    • PING_TRANSACTION:表示要調用pingBinder()
    • DUMP_TRANSACTION:獲取Binder內部狀態
    • SHELL_COMMAND_TRANSACTION:執行一個Shell命令
    • INTERFACE_TRANSACTION:詢問接收方的接口描述符號
    • TWEET_TRANSACTION:發送推文給目標對象
    • LIKE_TRANSACTION
  • data:傳入的數據,傳入的數據不能爲null,如果沒有要傳遞的數據,也要創建一個空對象
  • reply:返回的數據,如果回調未返回數據,可以爲空
  • flags:額外的標誌
    • 0:普通的遠程通信過程
    • FLAG_ONEWAY:單向通信,意味着client的transact()單向調用立即返回,而不用等待被叫者的結果。 僅在調用者和被調用者處於不同的進程時適用。
注意:IBinder的transact()是同步方法,被調用後會一直阻塞直到Binder.onTransact()調用完成後纔會返回結果。

3. 通過transact()發送的數據是一個Parcel對象,Parcel 中保存了數據以及描述數據的元數據,元數據在緩存區中保持了 IBinder 對象的引用,這樣不同進程都可以訪問同一個數據。

元數據用於管理緩衝區中的IBinder對象的引用,確保當IBinder寫入Parcel對象發送到另一個進程時,另一個進程發送同一個IBinder回到原來的進程,原進程接收的IBinder對象和開始發送的是同一個引用。

跨進程傳輸後引用未發生改變,使得IBinder/Binder對象在跨進程通信時可以作爲唯一標誌(用作token或其他目的)

4. 系統在每個進程中都有一個處理事物的線程池,這些線程用於調度其他進程對當前進程的跨進程訪問。

比如說進程 A 對進程 B 發起 IPC 時,A 中調用 transact() 的線程會阻塞。B 中的事物線程池收到 A 的 IPC,調用目標對象的 Binder.onTransact() 方法,然後返回帶結果的 Parcel。一旦接收到結果,A 中阻塞的線程得以繼續執行。

5. Binder支持進程間的遞歸調用。

比如,進程 A 向進程 B 發起 IPC,而進程 B 在其 Binder.onTransact() 中又用 transact() 向進程 A 發起 IPC,那麼進程 A 在等待它發出的調用返回的同時,也會響應 B 的調用,對調用的對象執行 Binder.onTransact() 方法。

6. 跨進程通信時,IBinder提供三種方法來檢測遠程進程是否可用

  • transact():在其進程不再存在的IBinder上調用它,則transact()方法將引發RemoteException異常。
  • pingBinder():如果遠程進程不再存在,則會返回false。
  • linkToDeath():可以使用linkToDeath()方法向IBinder註冊一個DeathRecipient,當其所在的進程消失時將調用它。
    /**
     * Check to see if the object still exists.
     * 
     * @return Returns false if the
     * hosting process is gone, otherwise the result (always by default
     * true) returned by the pingBinder() implementation on the other
     * side.
     */
    public boolean pingBinder();

    /**
     * Register the recipient for a notification if this binder
     * goes away.  If this binder object unexpectedly goes away
     * (typically because its hosting process has been killed),
     * then the given {@link DeathRecipient}'s
     * {@link DeathRecipient#binderDied DeathRecipient.binderDied()} method
     * will be called.
     * 
     * <p>You will only receive death notifications for remote binders,
     * as local binders by definition can't die without you dying as well.
     * 
     * @throws RemoteException if the target IBinder's
     * process has already died.
     * 
     * @see #unlinkToDeath
     */
    public void linkToDeath(DeathRecipient recipient, int flags)
            throws RemoteException;

    /**
     * Interface for receiving a callback when the process hosting an IBinder
     * has gone away.
     * 
     * @see #linkToDeath
     */
    public interface DeathRecipient {
        public void binderDied();
    }

2. Binder 代碼分析

上篇: Binder : AIDL的實踐中AIDL自動生成了接口文件,分析一下生成的代碼:

  • 生成的接口IUserManager繼承自IInterface,並聲明瞭在IUserManager.aidl中聲明的方法

    http://oo8dhx402.bkt.clouddn.com/18-4-28/40139917.jpg

    根據接口描述分析:

    • IInterface:Binder的基類接口,Binder通信中當定義一個新接口,必需繼承自IInterface。
    • IInterface.asBinder:返回值IBinder,檢索與此接口關聯的Binder對象。 您必須使用此方法進行簡單轉換,以便代理對象可以返回正確的結果。
  • IUserManager聲明瞭一個內部類Stub,Stub繼承自Binder實現IUserManager,是一個Binder類

    Stub內部類實現了IUserManager接口和接口的方法,並定義了幾個int類型的整數,整數主要用於標識在transact和onTransact中客戶端請求的到底是什麼方法。

  • 內部類Stub聲明一個內部代理類Proxy

    代理類Proxy主要完成方法實現及transact調用過程。

分析: 接口的核心實現是內部類Stub和Stub的內部代理類Proxy,着重分析兩個類的方法含義。

DESCRIPTOR

private static final java.lang.String DESCRIPTOR = "com.gqq.binderaidl.IUserManager";

Binder的唯一標識,一般用當前Binder的類名錶示,類似於Token。

asInterface(android.os.IBinder obj)

        /**
         * Cast an IBinder object into an com.gqq.binderaidl.IUserManager interface,
         * generating a proxy if needed.
         */
        public static com.gqq.binderaidl.IUserManager asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            // 嘗試檢索此Binder對象接口的本地實現。
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.gqq.binderaidl.IUserManager))) {
                return ((com.gqq.binderaidl.IUserManager) iin);
            }
            return new com.gqq.binderaidl.IUserManager.Stub.Proxy(obj);
        }

用於將服務端的Binder對象轉換爲客戶端所需要的AIDL接口類型的對象,轉換是區分進程的,如果在同一個進程,則返回服務端的Stub本身,否則返回的是系統封裝後的Stub.proxy對象,根據queryLocalInterface可以得知,如果返回null,必須要實例化代理類調用transact(),所以返回Stub.proxy對象。

asBinder

        @Override
        public android.os.IBinder asBinder() {
            return this;
        }

返回當前的Binder對象。

onTransact

        @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: {}
                case TRANSACTION_basicTypes: {}
                case TRANSACTION_addUser: {}
                case TRANSACTION_getUserList: {}
            }
            return super.onTransact(code, data, reply, flags);
        }

方法運行在服務端的Binder線程池中,當客戶端發起請求,遠程請求會通過底層封裝後交給此方法處理,此方法完成後將結果返回給發起請求者。

  • 根據code確定客戶端請求的是什麼目標方法。
  • 從data中取出目標方法所需要的參數,如果有參數的話,然後執行目標方法。
  • 目標方法執行完畢後,向reply中寫入返回值(如果目標方法有返回值的話)
  • 如果此方法返回false,客戶端請求會失敗,可以利用這個特性做權限驗證,來避免隨便一個進程都可以調用我們的服務。

Proxy#addUser()、getUserList()

兩個方法都運行在客戶端,大致實現:

  • 創建該方法需要的輸入型Parcel對象_data、輸出型Parcel對象_reply和返回值對象list
  • 把該方法的參數信息寫入_data中(如果有參數的話)
  • 調用transact()方法發起RPC(遠程過程調用)請求,同時線程掛起,等待服務端onTransact調用及RPC過程返回,當前線程繼續執行,並從reply中取出返回結果,最後返回_replay的數據,沒有返回值,就不返回。

注意:客戶端發起遠程請求時,當前線程會掛起直到服務端進程返回數據,所以如果一個遠程方法是耗時的操作,那麼不能在UI線程中發起此遠程請求。

工作流程圖:

創建的aidl文件只是爲了幫助生成接口,也可以不借助aid,自己創建接口。

在進程間通訊時兩個重要的方法:linkToDeathunlinkToDeath

上述有介紹說linkToDeath可以檢測服務端進程是否異常,如果斷裂,導致遠程調用失敗,而客戶端未知,功能會受影響。爲了解決這個問題,所以提供了兩個配對的方法:linkToDeathunlinkToDeath,通過linkToDeath可以給Binder設置一個死亡代理,當Binder死亡時,我們會收到通知。

    private IBinder.DeathRecipient deathRecipient = new IBinder.DeathRecipient() {
        @Override
        public void binderDied() {
            if (userManager == null) {
                return;
            }
            Log.i("TAG", "binderDied");
            userManager.asBinder().unlinkToDeath(deathRecipient, 0);
            userManager = null;
            // 重新綁定遠程Service
            bindUserService();
        }
    };
    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            userManager = IUserManager.Stub.asInterface(service);
            //  設置死亡代理
            try {
                service.linkToDeath(deathRecipient, 0);
            } catch (RemoteException e) {
                Log.i("TAG", "RemoteException");
                e.printStackTrace();
            }
        }
    }

也可以通過isBinderAlive判斷Binder是否死亡:

userManager.asBinder().isBinderAlive();

以上僅針對Java層進行的分析,有問題請指正~源碼分析原理還需要繼續努力!

推薦資料:

http://maoao530.github.io/2016/12/21/android-binder-01/

https://blog.csdn.net/u011240877/article/details/72801425

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章