Android Binder 機(jī)制

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

其實(shí)Android的Binder機(jī)制類似于:RPC(遠(yuǎn)程過程調(diào)用)发魄。如果你理解它盹牧,相信Binder機(jī)制就更容易理解了。

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

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

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

這個(gè)文件我們來分3個(gè)部分看:

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;
}

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

  1. 它繼承自android.os.IInterface汰寓。
  2. 定義了一個(gè)待實(shí)現(xiàn)的方法int getUserAge()
  3. 定義了一個(gè)Stub類。這個(gè)類繼承自Binder苹粟,并實(shí)現(xiàn)了IUserManager接口有滑。

int getUserAge()這個(gè)方法就是我們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都要實(shí)現(xiàn)的接口, 為什么呢毛好?舉一個(gè)我們都熟悉的場(chǎng)景 :

比如ApplicationThreadActivityManagerService(運(yùn)行在服務(wù)端進(jìn)程)就可以通過它來調(diào)用我們客戶端的方法苛秕。我們會(huì)把這些方法抽象為一個(gè)接口(IApplicationThread)肌访,這個(gè)接口可以理解為我們告訴服務(wù)端,你可以對(duì)客戶端執(zhí)行哪些操作想帅。

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

我們稱IApplicationThreadApplicationThread這個(gè)Binder的功能港准。 所以Binder除了可以理解為系統(tǒng)給我們提供的一個(gè)跨進(jìn)程通信的對(duì)象。 我們?cè)谟?code>Binder通信時(shí)咧欣,還可以說Binder是一個(gè)具有某些功能的一個(gè)對(duì)象浅缸。

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

重點(diǎn): Binder阁将、Binder的功能(IApplicationThread)、IInterface它們都在同一個(gè)對(duì)象上 -> ApplicationThread

Stub

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

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

即它是一個(gè)Binder,可以用來跨進(jìn)程通信做盅。它具有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 {...}

    }

我們還是一個(gè)一個(gè)的看一下:

DESCRIPTOR

基于我們前面的解釋,我們知道在跨進(jìn)程通信中Binder對(duì)象具有某種功能->IInterface吹榴。但是Binder通信機(jī)制中那么多Binder都有IInterface亭敢。那么系統(tǒng)怎么識(shí)別哪個(gè)Binder是哪個(gè)Binder呢?所以IInterface只是一個(gè)能力的抽象图筹,DESCRIPTOR就是來表示具體是哪一個(gè)功能IInterface帅刀。

TRANSACTION_userCount

即功能下的哪個(gè)操作。

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

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

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

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

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

asBinder()

自己返回自己娇妓,因?yàn)樽约罕旧砭褪莻€(gè)Binder呀。

onTransact()

當(dāng)其他進(jìn)程想跨進(jìn)程調(diào)用我這個(gè)Binder的功能時(shí)活鹰,必須通過這個(gè)方法來溝通哈恰。這個(gè)方法我們最后再來看。

asInterface(android.os.IBinder obj)

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

  • 調(diào)用者和Binder對(duì)象位于同一個(gè)進(jìn)程

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

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

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

  • 調(diào)用者和Binder對(duì)象不在同一個(gè)進(jìn)程

這時(shí)系統(tǒng)實(shí)際傳的是一個(gè)BinderProxy, 你可以理解為它是另一個(gè)進(jìn)程中的Binder的替身桑涎。我們就可以把它當(dāng)成另一個(gè)進(jìn)程的Binder彬向。我們看一下BinderProxyqueryLocalInterface()方法:

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

所以此時(shí)asInterface()返回的是: IUserManager.Stub.Proxy(obj), 即代理對(duì)象,它代理了BinderProxy攻冷。

IUserManager.Stub.Proxy

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

    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;
        }
    }

我們前面說了它其實(shí)是BinderProxy的代理。為什么要對(duì)BinderProxy加這個(gè)代理呢等曼?看一下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)用的遠(yuǎn)程Binder方法的getUserAge()對(duì)應(yīng)的描述符。

_data是序列化后的入?yún)ⅰ?code>_reply是序列化后的返回值禁谦⌒埠冢可以看到_data所攜帶的參數(shù)是需要序列化的,_reply所帶的內(nèi)容是被序列化的州泊,所以讀取要反序列化丧蘸。

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

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

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

省略了不重要的代碼触趴。即BinderProxy是通過transactNative來與遠(yuǎn)程Binder跨進(jìn)程通信的氮发。具體怎么實(shí)現(xiàn),這里就不追究了冗懦。

Stub.onTransact()

我們前面沒有看這個(gè)方法爽冕,這里我們來看一下:

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我們知道,如果不在同一個(gè)進(jìn)程披蕉,那么參數(shù)是被序列化后傳過來的颈畸,所以這個(gè)方法是用來對(duì)入?yún)⒆龇葱蛄谢?duì)返回值做序列化的没讲。

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

Binder機(jī)制.png

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

參考:

Android 進(jìn)階9:進(jìn)程通信之 AIDL 解析

Binder學(xué)習(xí)指南

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末眯娱,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子爬凑,更是在濱河造成了極大的恐慌徙缴,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,734評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件嘁信,死亡現(xiàn)場(chǎng)離奇詭異于样,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)潘靖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門穿剖,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人卦溢,你說我怎么就攤上這事糊余。” “怎么了单寂?”我有些...
    開封第一講書人閱讀 164,133評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵贬芥,是天一觀的道長。 經(jīng)常有香客問我凄贩,道長誓军,這世上最難降的妖魔是什么袱讹? 我笑而不...
    開封第一講書人閱讀 58,532評(píng)論 1 293
  • 正文 為了忘掉前任疲扎,我火速辦了婚禮,結(jié)果婚禮上捷雕,老公的妹妹穿的比我還像新娘椒丧。我一直安慰自己,他們只是感情好救巷,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,585評(píng)論 6 392
  • 文/花漫 我一把揭開白布壶熏。 她就那樣靜靜地躺著,像睡著了一般浦译。 火紅的嫁衣襯著肌膚如雪棒假。 梳的紋絲不亂的頭發(fā)上溯职,一...
    開封第一講書人閱讀 51,462評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音帽哑,去河邊找鬼谜酒。 笑死,一個(gè)胖子當(dāng)著我的面吹牛妻枕,可吹牛的內(nèi)容都是我干的僻族。 我是一名探鬼主播,決...
    沈念sama閱讀 40,262評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼屡谐,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼述么!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起愕掏,我...
    開封第一講書人閱讀 39,153評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤度秘,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后饵撑,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體敷钾,經(jīng)...
    沈念sama閱讀 45,587評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,792評(píng)論 3 336
  • 正文 我和宋清朗相戀三年肄梨,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了阻荒。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,919評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡众羡,死狀恐怖侨赡,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情粱侣,我是刑警寧澤羊壹,帶...
    沈念sama閱讀 35,635評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站齐婴,受9級(jí)特大地震影響油猫,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜柠偶,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,237評(píng)論 3 329
  • 文/蒙蒙 一情妖、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧诱担,春花似錦毡证、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,855評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春恤煞,著一層夾襖步出監(jiān)牢的瞬間屎勘,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,983評(píng)論 1 269
  • 我被黑心中介騙來泰國打工居扒, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留挑秉,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,048評(píng)論 3 370
  • 正文 我出身青樓苔货,卻偏偏與公主長得像犀概,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子夜惭,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,864評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容