Android Binder 機制

Android Binder機制的文章非常多,這篇文章主要是理一下我對Binder的理解。本文不是一篇介紹Binder的文章,也不是一篇探討Binder實現(xiàn)的文章。
本文會以AndroidStudio根據(jù)aidl接口自動產(chǎn)生的java文件來看Binder,進而來理解Binder機制。

其實Android的Binder機制類似于:RPC(遠程過程調(diào)用)。如果你理解它,相信Binder機制就更容易理解了。

首先我們使用AndroidStudio來定義一個aidl接口:

interface IUserManager {
    int getUserAge(in String userName);
}

然后我們來直接看一個由AndroidStudio根據(jù)自定義的aidl接口IUserManager產(chǎn)生的IUserManager.java文件。

這個文件我們來分3個部分看:

IUserManager接口結(jié)構(gòu)

public interface IUserManager extends android.os.IInterface {

    public static abstract class Stub extends android.os.Binder implements com.susion.demo.aidl.IUserManager {..}

    public int getUserAge(java.lang.String userName) throws android.os.RemoteException;
}

這個接口的結(jié)構(gòu)還是很簡單的:

  1. 它繼承自android.os.IInterface。
  2. 定義了一個待實現(xiàn)的方法int getUserAge()
  3. 定義了一個Stub類。這個類繼承自Binder,并實現(xiàn)了IUserManager接口。

int getUserAge()這個方法就是我們IUserManager接口的方法。而android.os.IInterface是什么呢?先看一下它在源碼中的定義:

/**
 * Base class for Binder interfaces.  When defining a new interface,
 * you must derive it from IInterface.
 */
public interface IInterface
{
    /**
     * Retrieve the Binder object associated with this interface.
     * You must use this instead of a plain cast, so that proxy objects
     * can return the correct result.
     */
    public IBinder asBinder(); //IBinder是Binder的抽象接口
}

即他是所有Binder都要實現(xiàn)的接口, 為什么呢?舉一個我們都熟悉的場景 :

比如ApplicationThreadActivityManagerService(運行在服務端進程)就可以通過它來調(diào)用我們客戶端的方法。我們會把這些方法抽象為一個接口(IApplicationThread),這個接口可以理解為我們告訴服務端,你可以對客戶端執(zhí)行哪些操作。

我們還知道ApplicationThread其實他就是一個Binder。所以這兩者一結(jié)合就可以這么說ApplicationThread: 客戶端提供給服務端一個Binder,通過這個Binder服務端可以對客戶端做一些操作,這些操作具體定義在IApplicationThread接口中。

我們稱IApplicationThreadApplicationThread這個Binder的功能。 所以Binder除了可以理解為系統(tǒng)給我們提供的一個跨進程通信的對象。 我們在用Binder通信時,還可以說Binder是一個具有某些功能的一個對象。

那么怎么表示Binder有功能呢? 即要繼承IInterface 。IInterface可以表示Binder有功能, 不然你想一個,那么多Binder都只實現(xiàn)自己的接口, 那么系統(tǒng)層就不好操作了,它總不能向下強轉(zhuǎn)為Binder吧,所以Android定義了一個更高層級的接口IInterface。描述Binder功能的接口必須繼承自這個接口。

重點: Binder、Binder的功能(IApplicationThread)、IInterface它們都在同一個對象上 -> ApplicationThread

Stub

它是IUserManager的內(nèi)部靜態(tài)類,看一下它的具體聲明:

static abstract class Stub extends android.os.Binder implements com.susion.demo.aidl.IUserManager 

即它是一個Binder,可以用來跨進程通信。它具有IUserManager定義的功能。

看一下它的具體結(jié)構(gòu):

    public static abstract class Stub extends android.os.Binder implements com.susion.demo.aidl.IUserManager {

        private static final java.lang.String DESCRIPTOR = "com.susion.demo.aidl.IUserManager";

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

        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        public static com.susion.demo.aidl.IUserManager asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.susion.demo.aidl.IUserManager))) {
                return ((com.susion.demo.aidl.IUserManager) iin);
            }
            return new com.susion.demo.aidl.IUserManager.Stub.Proxy(obj);
        }

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

        @Override
        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {...}

        private static class Proxy implements com.susion.demo.aidl.IUserManager {...}

    }

我們還是一個一個的看一下:

DESCRIPTOR

基于我們前面的解釋,我們知道在跨進程通信中Binder對象具有某種功能->IInterface。但是Binder通信機制中那么多Binder都有IInterface。那么系統(tǒng)怎么識別哪個Binder是哪個Binder呢?所以IInterface只是一個能力的抽象,DESCRIPTOR就是來表示具體是哪一個功能IInterface

TRANSACTION_userCount

即功能下的哪個操作。

Stub構(gòu)造函數(shù)

    public Stub() {
        this.attachInterface(this, DESCRIPTOR);
    }

即一個Stub(Binder)在構(gòu)造的時候,就標識好了自己的具體功能IInterface(IUserManager)。來看一下attachInterface(this, DESCRIPTOR)做了什么:

//Binder.java
public void attachInterface(@Nullable IInterface owner, @Nullable String descriptor) {
    mOwner = owner;  
    mDescriptor = descriptor;
}

即,Binder在內(nèi)部會用IInterface來保存自己的功能。和這個功能更對應的唯一描述descriptor,方便在通信的時候?qū)ふ摇?/p>

asBinder()

自己返回自己,因為自己本身就是個Binder呀。

onTransact()

當其他進程想跨進程調(diào)用我這個Binder的功能時,必須通過這個方法來溝通。這個方法我們最后再來看。

asInterface(android.os.IBinder obj)

即接收一個IBinder(這個IBinder是系統(tǒng)傳入的), 把這個IBinder轉(zhuǎn)化為它所具有功能接口。其實這里就是Binder跨進程通信的一個核心 。那怎么轉(zhuǎn)化的呢?

  • 調(diào)用者和Binder對象位于同一個進程

那么系統(tǒng)就會直接傳給你在這個進程創(chuàng)建的Stub(Binder)。所以 obj.queryLocalInterface(DESCRIPTOR):

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

即如果參數(shù)descriptor和這個Binder的功能唯一描述相同。就會返回Binder的功能mOwner

  • 調(diào)用者和Binder對象不在同一個進程

這時系統(tǒng)實際傳的是一個BinderProxy, 你可以理解為它是另一個進程中的Binder的替身。我們就可以把它當成另一個進程的Binder。我們看一下BinderProxyqueryLocalInterface()方法:

/**
* Retrieve a local interface - always null in case of a proxy
*/
public IInterface queryLocalInterface(String descriptor) {
    return null;
}

所以此時asInterface()返回的是: IUserManager.Stub.Proxy(obj), 即代理對象,它代理了BinderProxy

IUserManager.Stub.Proxy

它是Stub的靜態(tài)內(nèi)部類,如果調(diào)用者和Binder不在同一個進程的話,調(diào)用者拿到的實際是它:

    private static class Proxy implements com.didi.virtualapk.demo.aidl.IUserManager {
        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 getUserAge(java.lang.String userName) 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(userName);
                mRemote.transact(Stub.TRANSACTION_getUserAge, _data, _reply, 0);
                _reply.readException();
                _result = _reply.readInt();
            } finally {
                _reply.recycle();
                _data.recycle();
            }
            return _result;
        }
    }

我們前面說了它其實是BinderProxy的代理。為什么要對BinderProxy加這個代理呢?看一下getUserAge():

    public int getUserAge(java.lang.String userName) 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(userName);
            mRemote.transact(Stub.TRANSACTION_getUserAge, _data, _reply, 0);
            _reply.readException();
            _result = _reply.readInt();
        } finally {
            _reply.recycle();
            _data.recycle();
        }
        return _result;
    }

即是調(diào)用mRemote.transact()(BinderProxy的)方法。Stub.TRANSACTION_getUserAge是要調(diào)用的遠程Binder方法的getUserAge()對應的描述符。

_data是序列化后的入?yún)ⅰ?code>_reply是序列化后的返回值??梢钥吹?code>_data所攜帶的參數(shù)是需要序列化的,_reply所帶的內(nèi)容是被序列化的,所以讀取要反序列化。

所以IUserManager.Stub.Proxy類的作用就是在跨進程調(diào)用時對傳給mRemote(BinderProxy)的參數(shù)做序列化,對mRemote(BinderProxy)返回值做反序列化。參數(shù)的接受者和返回者是BinderProxy

具體調(diào)用Binder的能力是使用BinderProxytransact()方法,它是跨進程通信的核心 , 我們來看一下這個方法:

public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
    ...
    return transactNative(code, data, reply, flags); // native 方法
}

省略了不重要的代碼。即BinderProxy是通過transactNative來與遠程Binder跨進程通信的。具體怎么實現(xiàn),這里就不追究了。

Stub.onTransact()

我們前面沒有看這個方法,這里我們來看一下:

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_getUserAge: {
            data.enforceInterface(DESCRIPTOR);
            java.lang.String _arg0;
            _arg0 = data.readString();
            int _result = this.getUserAge(_arg0);
            reply.writeNoException();
            reply.writeInt(_result);
            return true;
        }
    }
    return super.onTransact(code, data, reply, flags);
}

根據(jù)IUserManager.Stub.Proxy我們知道,如果不在同一個進程,那么參數(shù)是被序列化后傳過來的,所以這個方法是用來對入?yún)⒆龇葱蛄谢?,并對返回值做序列化?/em>。

最后我們用一張圖來總結(jié)Binder進程通信機制 :

Binder機制.png

歡迎關(guān)注我的Android進階計劃看更多干貨

參考:

Android 進階9:進程通信之 AIDL 解析

Binder學習指南

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

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

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