Binder學習(三)通過AIDL分析Binder通訊流程

wustor發表於2017-11-26

概述

AIDL (Android Interface Definition Language) 是一種介面定義語言,用於生成可以在Android裝置上兩個程式之間進行PC的程式碼。如果在一個程式中(例如Activity)要呼叫另一個程式中(例如Service)物件的操作,就可以使用AIDL生成可序列化的引數,來完成程式間通訊,也就是說,為了簡化應用層進行IPC操作,Android提供了AIDL這門語言,在學習AIDL這門語言之前,我們來看一下Android中IPC的具體流程。

IPC具體的實際過程
IPC具體的實際過程

  • Client發起一個請求,阻塞
  • Client拿到服務端的Proxy,呼叫Proxy的相應方法
  • Proxy去跟Server進行互動,請求相應的結果
  • Proxy拿到結果返回給客戶端

Client跟Proxy進行互動的資料不需要進行序列化,但是Proxy跟Server進行互動的時候必須進行序列化跟反序列化

其實我們在實際進行IPC的時候並不需要關注Proxy以及Binder Driver,上述模型可以再次進行抽象

Binder抽象模型
Binder抽象模型

client端:BpBinder.transact()來傳送事務請求;
server端:BBinder.onTransact()會接收到相應事務。

正文

資料型別

AIDL預設支援如下資料型別:

  • 基本資料型別
  • List集合
  • Map集合
  • String型別
  • CharSequence型別

如果定義的型別不是AIDL預設支援的型別,則需要使用Parcelable進行序列化。一類是用來定義parcelable物件,以供其他AIDL檔案使用AIDL中非預設支援的資料型別的。

檔案型別

AIDL的檔案字尾名為.aidl,並不是之前的.java,不過在使用AIDL進行通訊的過程中,
aidl檔案只是用來定義資料型別跟介面。

使用方法

建立一個Parcelable 物件
public class People implements Parcelable {
    private int age;
    private String gender;
    private String hobby;
    //此處省略若干行程式碼
}複製程式碼
建立一個AIDL的類
package com.wustor.aidl;
// Declare any non-default types here with import statements
parcelable People;複製程式碼
建立一個AIDL的介面
package com.wustor.aidl;
// Declare any non-default types here with import statements
import com.wustor.aidl.People;
interface PeopleManager {
   List<People> getPeople();
  void addPeople(in People people);
}複製程式碼
檢視PeopleManager.java

Android系統會在".\app\build\generated\source\aidl\debug\com\wustor\aidl"的目錄下生成一個PeopleManager.java檔案,這個類就是AIDL的核心,這個類是一個介面,下面先看一下這個介面的結構圖:

PeopleManager
PeopleManager

PeopleManager內部實現了PeopleManager.aidl的兩個方法,並且在內部建立了一個叫Stub的內部類,同時Stub也自己維護了一個叫做Proxy的內部類,通過前面對Binder機制的原理分析,我們其實可以很明確的知道,Proxy就是服務端的代理類,他作為一個 中間代理,承載了Client與Server之間的轉化,而Stub類的onTransact方法就是用來接收Proxy的輸入並且把請求結果返回,從而達到代理的作用,所以分析AIDL實際上只需要注重分析一下asInterface,onTransact這兩個方法以及Proxy代理類即可。

asBinder
    @Override
        public android.os.IBinder asBinder() {
            return this;
        }複製程式碼

返回當前Binder物件

asInterface

此方法位於Client端

 /**
         * Cast an IBinder object into an com.wustor.aidl.PeopleManager interface,
         * generating a proxy if needed.
         */
        public static com.wustor.aidl.PeopleManager asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            //檢視本地是否存在這個類,不存在的話就重新建立
            if (((iin != null) && (iin instanceof com.wustor.aidl.PeopleManager))) {
                return ((com.wustor.aidl.PeopleManager) iin);
            }
            return new com.wustor.aidl.PeopleManager.Stub.Proxy(obj);
        }複製程式碼

queryLocalInterface

  public IInterface queryLocalInterface(String descriptor) {
        if (mDescriptor.equals(descriptor)) {
            return mOwner;
        }
        return null;
    }

    public void attachInterface(IInterface owner, String descriptor) {
        mOwner = owner;
        mDescriptor = descriptor;
    }複製程式碼

首先會呼叫queryLocalInterface來判斷descriptor跟mDescriptor是否相等,我們通過檢視Binder的原始碼可以看到descriptor就是當前PeopleManager的類名,那麼通過傳遞過來的Binder物件查詢這個類名,實際上就是判斷Server端的BookManager跟Client是不是相同,如果相同就說明Client跟Server是在同一個程式,如果在同一個程式,那麼就直接返回當前的IInterface ,否則返回null,那麼Client端就會自己建立一個Proxy的代理類。

Proxy

getPeople()

    static final int TRANSACTION_getPeople = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    static final int TRANSACTION_addPeople = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
 @Override
  public java.util.List<com.wustor.aidl.People> getPeople() throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.util.List<com.wustor.aidl.People> _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    //呼叫transact方法,傳遞引數
                    mRemote.transact(Stub.TRANSACTION_getPeople, _data, _reply, 0);
                    _reply.readException();
                    //拿到返回結果
                    _result = _reply.createTypedArrayList(com.wustor.aidl.People.CREATOR);
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                //返回引數給Client
                return _result;
            }複製程式碼

在Client呼叫,執行Transact方法,當前執行緒阻塞,服務端的onTransact方法會被呼叫,從reply中拿到返回值後,執行緒繼續執行。

addPeoplet

 @Override
            public void addPeople(com.wustor.aidl.People people) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    if ((people != null)) {
                        _data.writeInt(1);
                        people.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                    mRemote.transact(Stub.TRANSACTION_addPeople, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }複製程式碼

在Client執行,跟getPeople()方法基本一致,唯一的區別是此方法沒有返回值,所以不需要回寫Client

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: {
                    reply.writeString(DESCRIPTOR);
                    return true;
                }
                case TRANSACTION_getPeople: {
                    data.enforceInterface(DESCRIPTOR);
                    java.util.List<com.wustor.aidl.People> _result = this.getPeople();
                    reply.writeNoException();
                    reply.writeTypedList(_result);
                    return true;
                }
                case TRANSACTION_addPeople: {
                    data.enforceInterface(DESCRIPTOR);
                    com.wustor.aidl.People _arg0;
                    if ((0 != data.readInt())) {
                        _arg0 = com.wustor.aidl.People.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    this.addPeople(_arg0);
                    reply.writeNoException();
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }複製程式碼

執行在Server端,當Client端發起跨程式請求的時候,系統底層會返回Proxy代理,然後通過代理執行Client的方法,就會呼叫此方法。注意這個方法會從data取傳遞過來的引數,然後通過code判斷需要執行哪一個方法,執行完畢後,就返回值寫入reply中,此方法的返回值表明IPC請求是否成功。

總結

通過分析AIDL的原理,從而可以進一步理解Binder機制,總結一下,Android中利用Binder機制。

BInder工作機制
BInder工作機制

通過觀察這張圖,我們可以AIDL底層對Binder機制進行了封裝,讓Android中的IPC通訊機制更加簡單方便,當然,我們也可以自己動手寫,當然Android中還有別的很多IPC通訊方式,例如Messenger等,。如果想實現Binder機制進行通訊,只需要Server(在Android裡面大多數使用Service來建立一個Server)端返回一個Binder物件,然後將呼叫asInterface將Binder物件傳遞過來即可。

參考資料

Android開發藝術探索

相關文章