3分鐘帶你看懂a(chǎn)ndroid中的Binder機(jī)制

一.引言

最近一段時(shí)間由于工作,接觸到framework部分比較多一點(diǎn),也難免要和Binder打一些交道,也整理了一些相關(guān)知識(shí),但準(zhǔn)備寫這篇文章時(shí),還是有些慌。而且關(guān)于整個(gè)Binder機(jī)制的復(fù)雜程度不是三言兩語(yǔ)能描敘清楚的,也害怕自己的理解有些偏差,誤導(dǎo)一些朋友(ps:反正也沒人看....扎心)所以也參考了很多資料。

本文主要站在Android開發(fā)的角度來大致解析下Binder在java層的一些知識(shí)原理,給大家腦子形成一個(gè)完整的概念,比如AIDL的實(shí)現(xiàn)原理,Binder是怎么通信的等等,文章文字較多,請(qǐng)耐心觀看

二.Binder概述

2.1 Android為什么選擇Binder

Android是基于Linux內(nèi)核的,所以Android要實(shí)現(xiàn)進(jìn)程間的通信,其實(shí)大可使用linux原有的一些手段,比如管道,共享內(nèi)存,socket等方式,但是Android還是采用了Binder作為主要機(jī)制,說明Binder具有無可比擬的優(yōu)勢(shì)。

其實(shí)進(jìn)程通信大概就兩個(gè)方面因素,一者性能方面,傳輸效率問題,傳統(tǒng)的管道隊(duì)列模式采用內(nèi)存緩沖區(qū)的方式,數(shù)據(jù)先從發(fā)送方緩存區(qū)拷貝到內(nèi)核開辟的緩存區(qū)中,然后再?gòu)膬?nèi)核緩存區(qū)拷貝到接收方緩存區(qū),至少有兩次拷貝過程,而socket都知道傳輸效率低,開銷大,用于跨網(wǎng)絡(luò)進(jìn)程交互比較多,共享內(nèi)存雖然無需拷貝。

二者這是安全問題,Android作為一個(gè)開放式,擁有眾多開發(fā)者的的平臺(tái),應(yīng)用程序的來源廣泛,確保終端安全是非常重要的,傳統(tǒng)的IPC通信方式?jīng)]有任何措施,基本依靠上層協(xié)議,其一無法確認(rèn)對(duì)方可靠的身份,Android為每個(gè)安裝好的應(yīng)用程序分配了自己的UID,故進(jìn)程的UID是鑒別進(jìn)程身份的重要標(biāo)志,傳統(tǒng)的IPC要發(fā)送類似的UID也只能放在數(shù)據(jù)包里,但也容易被攔截,惡意進(jìn)攻,socket則需要暴露自己的ip和端口,知道這些惡意程序則可以進(jìn)行任意接入。

綜上所述,Android需要一種高效率,安全性高的進(jìn)程通信方式,也就是Binder,Binder只需要一次拷貝,性能僅次于共享內(nèi)存,而且采用的傳統(tǒng)的C/S結(jié)構(gòu),穩(wěn)定性也是沒得說,發(fā)送添加UID/PID,安全性高。

2.2 Binder實(shí)現(xiàn)機(jī)制

2.2.1進(jìn)程隔離

我們知道進(jìn)程之間是無法直接進(jìn)行交互的,每個(gè)進(jìn)程獨(dú)享自己的數(shù)據(jù),而且操作系統(tǒng)為了保證自身的安全穩(wěn)定性,將系統(tǒng)內(nèi)核空間和用戶空間分離開來,保證用戶程序進(jìn)程崩潰時(shí)不會(huì)影響到整個(gè)系統(tǒng),簡(jiǎn)單的說就是,內(nèi)核空間(Kernel)是系統(tǒng)內(nèi)核運(yùn)行的空間,用戶空間(UserSpace)是用戶程序運(yùn)行的空間。為了保證安全性,它們之間是隔離的,所以用戶空間的進(jìn)程要進(jìn)行交互需要通過內(nèi)核空間來驅(qū)動(dòng)整個(gè)過程。

2.2.2 C/S結(jié)構(gòu)

Binder是基于C/S機(jī)制的,要實(shí)現(xiàn)這樣的機(jī)制,server必須需要有特定的節(jié)點(diǎn)來接受到client的請(qǐng)求,也就是入口地址,像輸入一個(gè)網(wǎng)址,通過DNS解析出對(duì)應(yīng)的ip,然后進(jìn)行訪問,這個(gè)就是server提供出來的節(jié)點(diǎn)地址,而Binder而言的話,與傳統(tǒng)的C/S不太一樣,Binder本身來作為Server中提供的節(jié)點(diǎn),client拿到Binder實(shí)體對(duì)象對(duì)應(yīng)的地址去訪問Server,對(duì)于client而言,怎么拿到這個(gè)地址并建立起整個(gè)通道是整個(gè)交互的關(guān)鍵所在,而且Binder作為一個(gè)Server中的實(shí)體,對(duì)象提供一系列的方法來實(shí)現(xiàn)服務(wù)端和客戶端之間的請(qǐng)求,只要client拿到這個(gè)引用就可以或者一個(gè)有著該方法代理對(duì)象的引用,就可以進(jìn)行通信了。

引用Android Bander設(shè)計(jì)與實(shí)現(xiàn) - 設(shè)計(jì)篇一段話來總結(jié):

面向?qū)ο笏枷氲囊雽⑦M(jìn)程間通信轉(zhuǎn)化為通過對(duì)某個(gè)Binder對(duì)象的引用調(diào)用該對(duì)象的方法,而其獨(dú)特之處在于Binder對(duì)象是一個(gè)可以跨進(jìn)程引用的對(duì)象,它的實(shí)體位于一個(gè)進(jìn)程中,而它的引用卻遍布于系統(tǒng)的各個(gè)進(jìn)程之中。最誘人的是,這個(gè)引用和java里引用一樣既可以是強(qiáng)類型,也可以是弱類型,而且可以從一個(gè)進(jìn)程傳給其它進(jìn)程,讓大家都能訪問同一Server,就象將一個(gè)對(duì)象或引用賦值給另一個(gè)引用一樣。Binder模糊了進(jìn)程邊界,淡化了進(jìn)程間通信過程,整個(gè)系統(tǒng)仿佛運(yùn)行于同一個(gè)面向?qū)ο蟮某绦蛑小P涡紊腂inder對(duì)象以及星羅棋布的引用仿佛粘接各個(gè)應(yīng)用程序的膠水,這也是Binder在英文里的原意。

2.2.3 Binder通信模型

Binder基于C/S的結(jié)構(gòu)下,定義了4個(gè)角色:Server、Client、ServerManager、Binder驅(qū)動(dòng),其中前三者是在用戶空間的,也就是彼此之間無法直接進(jìn)行交互,Binder驅(qū)動(dòng)是屬于內(nèi)核空間的,屬于整個(gè)通信的核心,雖然叫驅(qū)動(dòng),但是實(shí)際上和硬件沒有太大關(guān)系,只是實(shí)現(xiàn)的方式和驅(qū)動(dòng)差不多,驅(qū)動(dòng)負(fù)責(zé)進(jìn)程之間Binder通信的建立,Binder在進(jìn)程之間的傳遞,Binder引用計(jì)數(shù)管理,數(shù)據(jù)包在進(jìn)程之間的傳遞和交互等一系列底層支持。

ServerManager的作用?

我們知道ServerManager也是屬于用戶空間的一個(gè)進(jìn)程,主要作用就是作為Server和client的橋梁,client可以ServerManager拿到Server中Binder實(shí)體的引用,這么說可能有點(diǎn)模糊,舉個(gè)簡(jiǎn)單的例子,我們?cè)L問,www.baidu.com,百度首頁(yè)頁(yè)面就顯示出來了,首先我們知道,這個(gè)頁(yè)面肯定是發(fā)布在百度某個(gè)服務(wù)器上的,DNS通過你這個(gè)地址,解析出對(duì)應(yīng)的ip地址,再去訪問對(duì)應(yīng)的頁(yè)面,然后再把數(shù)據(jù)返回給客戶端,完成交互。這個(gè)和Binder的C/S非常類似,這里的DNS就是對(duì)應(yīng)的ServerManager,首先,Server中的Binder實(shí)體對(duì)象,將自己的引用(也就是ip地址)注冊(cè)到ServerManager,client通過特定的key(也就是百度這個(gè)網(wǎng)址)和這個(gè)引用進(jìn)行綁定,ServerManager內(nèi)部自己維護(hù)一個(gè)類似MAP的表來一一對(duì)應(yīng),通過這個(gè)key就可以向ServerManager拿到Server中Binder的引用,對(duì)應(yīng)到Android開發(fā)中,我們知道很多系統(tǒng)服務(wù)都是通過Binder去和AMS進(jìn)行交互的,比如獲取音量服務(wù):

AudioManager am = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE);

細(xì)心的朋友應(yīng)該發(fā)現(xiàn)ServerManager和Server也是兩個(gè)不同的進(jìn)程呀,Server要向ServerManager去注冊(cè)不是也要涉及到進(jìn)程間的通信嗎,當(dāng)前實(shí)現(xiàn)進(jìn)程間通信又要用到進(jìn)程間的通信,你這不是扯犢子嗎....莫急莫急,Binder的巧妙之處在于,當(dāng)ServerManager作為Serve端的時(shí)候,它提供的Binder比較特殊,它沒有名字也不需要注冊(cè),當(dāng)一個(gè)進(jìn)程使用BINDER_SET_CONTEXT_MGR命令將自己注冊(cè)成SMgr時(shí)Binder驅(qū)動(dòng)會(huì)自動(dòng)為它創(chuàng)建Binder實(shí)體,這個(gè)Binder的引用在所有Client中都固定為0而無須通過其它手段獲得。也就是說,一個(gè)Server若要向ServerManager注冊(cè)自己Binder就必需通過0這個(gè)引用號(hào)和ServerManager的Binder通信,有朋友又要問了,server和client屬于兩個(gè)不同的進(jìn)程,client怎么能拿到server中對(duì)象,不妨先看看下面的交互圖

image

從上圖很清晰的可以看出來整個(gè)的交互過程,原本從SM中拿到binder的引用,通過Binder驅(qū)動(dòng)層的處理之后,返回給了client一個(gè)代理對(duì)象,實(shí)際上如果client和server處于同一個(gè)進(jìn)程,返回的就是當(dāng)前binder對(duì)象,如果client和server不處于同一個(gè)進(jìn)程,返回給client的就是一個(gè)代理對(duì)象,這一點(diǎn),有興趣可以看下源碼。

2.2.4 Binder角色的定位

Binder本質(zhì)上只是提供了一種通信的方式,和我們具體要實(shí)現(xiàn)的內(nèi)容沒有關(guān)系,為了實(shí)現(xiàn)這個(gè)服務(wù),我們需要定義一些接口,讓client能夠遠(yuǎn)程調(diào)用服務(wù),因?yàn)槭强邕M(jìn)程,這時(shí)候就要設(shè)計(jì)到代理模式,以接口函數(shù)位基準(zhǔn),client和server去實(shí)現(xiàn)接口函數(shù),Server是服務(wù)真正的實(shí)現(xiàn),client作為一個(gè)遠(yuǎn)程的調(diào)用。

  • 從Server進(jìn)程來看,Binder是存在的實(shí)體對(duì)象,client通過transact()函數(shù),經(jīng)過Binder驅(qū)動(dòng),最終回調(diào)到Binder實(shí)體的onTransact()函數(shù)中。
  • 從 Client進(jìn)程的角度看,Binder 指的是對(duì) Binder 代理對(duì)象,是 Binder 實(shí)體對(duì)象的一個(gè)遠(yuǎn)程代理,通過Binder驅(qū)動(dòng)進(jìn)行交互

3.手寫進(jìn)程通信

上面說了那么多,大家伙也看累了,下面通過代碼的形式讓大家對(duì)Binder加深點(diǎn)理解,日常開發(fā)中,涉及到進(jìn)程間通信的話,我們首先想到的可能就是AIDL,但不知道有沒有和我感覺一樣的朋友。。第一次寫AIDL是蒙蔽的,通過.aidl文件,編譯器自動(dòng)生成代碼,生成一個(gè)java文件,里面又有Stub類,里面還有Proxy類,完全不理解里面的機(jī)制,確實(shí)不便于我們理解學(xué)習(xí),為了加深理解,我們拋棄aidl,手寫一個(gè)通信代碼。

首先我們要定義一個(gè)接口服務(wù),也就是上述的服務(wù)端要具備的能力來提供給客戶端,定義一個(gè)接口繼承IInterface,代表了服務(wù)端的能力

public interface PersonManger extends IInterface {
    void addPerson(Person mPerson);
    List<Person> getPersonList();
}

接下來我們就要定義一個(gè)Server中的Binder實(shí)體對(duì)象了,首先肯定要繼承Binder,其次需要實(shí)現(xiàn)上面定義好的服務(wù)接口

public abstract class BinderObj extends Binder implements PersonManger {
    public static final String DESCRIPTOR = "com.example.taolin.hellobinder";
    public static final int TRANSAVTION_getPerson = IBinder.FIRST_CALL_TRANSACTION;
    public static final int TRANSAVTION_addPerson = IBinder.FIRST_CALL_TRANSACTION + 1;
    public static PersonManger asInterface(IBinder mIBinder){
        IInterface iInterface = mIBinder.queryLocalInterface(DESCRIPTOR);
        if (null!=iInterface&&iInterface instanceof PersonManger){
            return (PersonManger)iInterface;
        }
        return new Proxy(mIBinder);
    }
    @Override
    protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException {
        switch (code){
            case INTERFACE_TRANSACTION:
                reply.writeString(DESCRIPTOR);
                return true;

            case TRANSAVTION_getPerson:
                data.enforceInterface(DESCRIPTOR);
                List<Person> result = this.getPersonList();
                reply.writeNoException();
                reply.writeTypedList(result);
                return true;

            case TRANSAVTION_addPerson:
                data.enforceInterface(DESCRIPTOR);
                Person arg0 = null;
                if (data.readInt() != 0) {
                    arg0 = Person.CREATOR.createFromParcel(data);
                }
                this.addPerson(arg0);
                reply.writeNoException();
                return true;
        }
        return super.onTransact(code, data, reply, flags);

    }

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

首先我們看asInterface方法,Binder驅(qū)動(dòng)傳來的IBinder對(duì)象,通過queryLocalInterface方法,查找本地Binder對(duì)象,如果返回的就是PersonManger,說明client和server處于同一個(gè)進(jìn)程,直接返回,如果不是,返回給一個(gè)代理對(duì)象。

當(dāng)然作為代理對(duì)象,也是需要實(shí)現(xiàn)服務(wù)接口

public class Proxy implements PersonManger {
    private IBinder mIBinder;
    public Proxy(IBinder mIBinder) {
        this.mIBinder =mIBinder;
    }

    @Override
    public void addPerson(Person mPerson) {
        Parcel data = Parcel.obtain();
        Parcel replay = Parcel.obtain();

        try {
            data.writeInterfaceToken(DESCRIPTOR);
            if (mPerson != null) {
                data.writeInt(1);
                mPerson.writeToParcel(data, 0);
            } else {
                data.writeInt(0);
            }
            mIBinder.transact(BinderObj.TRANSAVTION_addPerson, data, replay, 0);
            replay.readException();
        } catch (RemoteException e){
            e.printStackTrace();
        } finally {
            replay.recycle();
            data.recycle();
        }
    }

    @Override
    public List<Person> getPersonList() {
        Parcel data = Parcel.obtain();
        Parcel replay = Parcel.obtain();
        List<Person> result = null;
        try {
            data.writeInterfaceToken(DESCRIPTOR);
            mIBinder.transact(BinderObj.TRANSAVTION_getPerson, data, replay, 0);
            replay.readException();
            result = replay.createTypedArrayList(Person.CREATOR);
        }catch (RemoteException e){
            e.printStackTrace();
        } finally{
            replay.recycle();
            data.recycle();
        }
        return result;
    }

    @Override
    public IBinder asBinder() {
        return null;
    }
}

這里的代理對(duì)象實(shí)質(zhì)就是client最終拿到的代理服務(wù),通過這個(gè)就可以和Server進(jìn)行通信了,首先通過Parcel將數(shù)據(jù)序列化,然后調(diào)用 remote.transact()將方法code,和data傳輸過去,對(duì)應(yīng)的會(huì)回調(diào)在在Server中的onTransact()中

然后是我們的Server進(jìn)程,onBind方法返回mStub對(duì)象,也就是Server中的Binder實(shí)體對(duì)象

public class ServerSevice extends Service {
    private static final String TAG = "ServerSevice";
    private List<Person> mPeople = new ArrayList<>();


    @Override
    public void onCreate() {
        mPeople.add(new Person());
        super.onCreate();
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mStub;
    }
    private BinderObj mStub = new BinderObj() {
        @Override
        public void addPerson(Person mPerson) {
            if (mPerson==null){
                mPerson = new Person();
                Log.e(TAG,"null obj");
            }
            mPeople.add(mPerson);
            Log.e(TAG,mPeople.size()+"");
        }

        @Override
        public List<Person> getPersonList() {
            return mPeople;
        }
    };
}

最終我們?cè)诳蛻舳诉M(jìn)程,bindService傳入一個(gè)ServiceConnection對(duì)象,在與服務(wù)端建立連接時(shí),通過我們定義好的BinderObj的asInterface方法返回一個(gè)代理對(duì)象,再調(diào)用方法進(jìn)行交互

public class MainActivity extends AppCompatActivity {
    private boolean isConnect = false;
    private static final String TAG = "MainActivity";
    private PersonManger personManger;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        start();
        findViewById(R.id.textView).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (personManger==null){
                    Log.e(TAG,"connect error");
                    return;
                }
                personManger.addPerson(new Person());
                Log.e(TAG,personManger.getPersonList().size()+"");
            }
        });
    }

    private void start() {
        Intent intent = new Intent(this, ServerSevice.class);
        bindService(intent,mServiceConnection,Context.BIND_AUTO_CREATE);
    }
    private ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.e(TAG,"connect success");
            isConnect = true;
            personManger = BinderObj.asInterface(service);
            List<Person> personList = personManger.getPersonList();
            Log.e(TAG,personList.size()+"");
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.e(TAG,"connect failed");
            isConnect = false;
        }
    };
}

這樣的話,一次完成的進(jìn)程間的交互就完成了~是不是感覺沒有想象中那么難,最后建議大家在不借助 AIDL 的情況下手寫實(shí)現(xiàn) Client 和 Server 進(jìn)程的通信,加深對(duì) Binder 通信過程的理解。

本文在寫作過程中參考了蠻多的文章和源碼,感謝大佬們的無私奉獻(xiàn),溜了溜了~

參考文章

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

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

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