Android Binder解密

前言

稍微看過(guò)Android FrameWork層的人應(yīng)該都知道Binder究驴,因?yàn)閍pp與系統(tǒng)服務(wù)之間的通信基本上都是建立在Binder的基礎(chǔ)上泽谨。之前對(duì)Binder也是云里霧里狸演,似懂非懂蕉斜,于是花了不少時(shí)間鸟辅,看了很多資料和源碼茅糜,才大致了解了Binder通信的原理橄杨,總結(jié)出來(lái)秘症,如有錯(cuò)誤,還望指正式矫。

簡(jiǎn)介

Binder是什么乡摹?Binder是為跨進(jìn)程通信而生的產(chǎn)物。眾所周知采转,我們的app都是由Zygote進(jìn)程fork出來(lái)的聪廉,每個(gè)app都運(yùn)行在單獨(dú)的進(jìn)程中,一個(gè)app想與另外一個(gè)app進(jìn)行通信故慈,只能采用跨進(jìn)程的方式板熊,傳統(tǒng)的Linux跨進(jìn)程方式有如下幾種:管道、信號(hào)量察绷、共享內(nèi)存及Socket等干签,Android系統(tǒng)建立在Linux的基礎(chǔ)上,但其采用的是Binder來(lái)進(jìn)行IPC的拆撼。

接下來(lái)讓我們用一個(gè)小Demo展示一下如何用Binder進(jìn)行跨進(jìn)程通信容劳。

實(shí)例

既然是跨進(jìn)程通信,那么至少得有兩個(gè)進(jìn)程闸度,最簡(jiǎn)單的方式就是指定activity的process屬性竭贩,但是本文為了更清楚的講解binder,采用的方式是建立兩個(gè)app莺禁,一個(gè)作為服務(wù)端留量,一個(gè)作為客戶端。服務(wù)端提供一個(gè)簡(jiǎn)單的生詞本功能,客戶端可以向服務(wù)端插入和查詢生詞肪获。
使用Binder通信主要有以下幾個(gè)步驟:

Step1:編寫AIDL文件

想要使用Binder寝凌,必須要先了解AIDL(Android Interface Definition Language)柒傻,也就是接口定義語(yǔ)言孝赫,提供接口給遠(yuǎn)程調(diào)用者。
為了給客戶端提供生詞本的調(diào)用接口红符,我們?cè)?src/main目錄下先新建一個(gè)文件夾aidl,并新建一個(gè)aidl文件IDictionaryManager.aidl青柄。

// IDictionaryManager.aidl
package com.wanginbeijing.dictionaryserver;

interface IDictionaryManager {
    void add(String chinese,String english);
    String query(String chinese);
}

接口中提供了兩個(gè)方法:add()和query(),分別作為插入和查詢操作预侯。Build一下工程致开,android studio會(huì)自動(dòng)為我們生成一個(gè)java類:IDictionaryManager.java。


我們來(lái)看看這個(gè)java類里面都寫了什么

public interface IDictionaryManager extends android.os.IInterface {

    public static abstract class Stub extends android.os.Binder implements com.wanginbeijing.dictionaryserver.IDictionaryManager {
        private static final java.lang.String DESCRIPTOR = "com.wanginbeijing.dictionaryserver.IDictionaryManager";

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


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

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

        @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_add: {
                    data.enforceInterface(DESCRIPTOR);
                    java.lang.String _arg0;
                    _arg0 = data.readString();
                    java.lang.String _arg1;
                    _arg1 = data.readString();
                    this.add(_arg0, _arg1);
                    reply.writeNoException();
                    return true;
                }
            case TRANSACTION_query: {
                    data.enforceInterface(DESCRIPTOR);
                    java.lang.String _arg0;
                    _arg0 = data.readString();
                    java.lang.String _result = this.query(_arg0);
                    reply.writeNoException();
                    reply.writeString(_result);
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements com.wanginbeijing.dictionaryserver.IDictionaryManager {
            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 void add(java.lang.String chinese, java.lang.String english) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeString(chinese);
                    _data.writeString(english);
                    mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }

            @Override
            public java.lang.String query(java.lang.String chinese) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.lang.String _result;
                try {
                 _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeString(chinese);
                    mRemote.transact(Stub.TRANSACTION_query, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readString();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }

        static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_query = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    }

    public void add(java.lang.String chinese, java.lang.String english) throws android.os.RemoteException;

    public java.lang.String query(java.lang.String chinese) throws android.os.RemoteException;
}

這個(gè)接口類比較長(zhǎng)萎馅,繼承了android.os.IInterface這個(gè)接口双戳。這個(gè)類簡(jiǎn)化的結(jié)構(gòu)大致如下:

public interface IDictionaryManager extends android.os.IInterface {

    public static abstract class Stub extends android.os.Binder implements com.wanginbeijing.dictionaryserver.IDictionaryManager {
    
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }


        public static com.wanginbeijing.dictionaryserver.IDictionaryManager asInterface(android.os.IBinder obj) 
        
        @Override
        public android.os.IBinder asBinder() 
        
        @Override
        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)
        
        private static class Proxy implements com.wanginbeijing.dictionaryserver.IDictionaryManager {
            private android.os.IBinder mRemote;

            Proxy(android.os.IBinder remote)
            
            @Override
            public android.os.IBinder asBinder() 
            
            public java.lang.String getInterfaceDescriptor() 
            
            @Override
            public void add(java.lang.String chinese, java.lang.String english)
            
            @Override
            public java.lang.String query(java.lang.String chinese)  
      }

        static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_query = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    }

    public void add(java.lang.String chinese, java.lang.String english) 

    public java.lang.String query(java.lang.String chinese)
}

該類首先包含了一個(gè)抽象內(nèi)部類:Stub, 該類繼承自Binder并實(shí)現(xiàn)了IDictionary接口。在Stub的內(nèi)部糜芳,又包含了一個(gè)靜態(tài)內(nèi)部類:Proxy飒货,Proxy類同樣實(shí)現(xiàn)了IDictionary接口。

Step2:定義一個(gè)Service峭竣,用于客戶端連接

public class DictionaryManagerService extends Service {
    private Map<String, String> mMap = new HashMap<>();

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return new IDictionaryManager.Stub() {
            @Override
            public void add(String chinese, String english) throws RemoteException {
                mMap.put(chinese, english);
                Log.e("DictionaryManager", "add new word");
            }

            @Override
            public String query(String chinese) throws RemoteException {
                return mMap.get(chinese);
            }
        };
    }
}

該類中定義了一個(gè)HashMap用來(lái)保存生詞塘辅,并重寫了Service中的onBind方法,在onBinder方法中皆撩,返回了一個(gè)繼承自IDictionaryManager.Stub的匿名內(nèi)部類扣墩,并重寫了IDictionaryManager接口中add和query方法,實(shí)現(xiàn)真正的生詞本業(yè)務(wù)邏輯扛吞。

Step3:客戶端連接服務(wù)端Service

客戶端要調(diào)用服務(wù)端接口呻惕,先要將服務(wù)端定義好的aidl文件拷貝到客戶端相同目錄下,并build生成java文件滥比。然后開始連接服務(wù)器端:

public class MainActivity extends Activity {

    private IDictionaryManager mDictionaryManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Intent intent = new Intent();
        intent.setAction("android.intent.action.DictionaryManagerService");
        intent.setPackage("com.wanginbeijing.dictionaryserver");
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
        //添加一個(gè)新單詞
        findViewById(R.id.btn_add).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                try {
                    mDictionaryManager.add("你好", "Hello");
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        });

        //查詢單詞
        findViewById(R.id.btn_query).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                try {
                    String english = mDictionaryManager.query("你好");
                    Toast.makeText(MainActivity.this, english, Toast.LENGTH_SHORT).show();
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        });
    }

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            IDictionaryManager dictionaryManager = IDictionaryManager.Stub.asInterface(iBinder);
            try {
                mDictionaryManager = dictionaryManager;
                Toast.makeText(MainActivity.this, "connect success", Toast.LENGTH_SHORT).show();
            } catch (Exception e) {
                Toast.makeText(MainActivity.this, "connect failed", Toast.LENGTH_SHORT).show();
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
        }
    };
}

客戶端的MainActivty的onCreate()方法中亚脆,首先自動(dòng)連接遠(yuǎn)程service,拿到遠(yuǎn)程service傳回的Binder對(duì)象后守呜,強(qiáng)轉(zhuǎn)為IDictionaryService類型的變量mDictionaryManager型酥。然后在add按鈕和query按鈕的監(jiān)聽事件中分別添加和查詢單詞(你好,Hello)查乒。下面展示一下操作Demo.



可以看見弥喉,在連接遠(yuǎn)端service成功后,首先點(diǎn)擊add按鈕插入單詞玛迄,接著調(diào)用query接口由境,可以成功查詢到剛剛插入的單詞。

以上就是使用Binder進(jìn)行IPC的主要過(guò)程,但是僅僅掌握了使用方法怎么能滿足我們的好奇心呢虏杰,我們還必須要挖挖它的原理讥蟆。

原理

java層

說(shuō)到原理,還得先從代碼看起纺阔,回到上述所說(shuō)的Android Studio自動(dòng)生成的那個(gè)java類:IDictionaryManager瘸彤。剛才已經(jīng)說(shuō)過(guò),該類中有一個(gè)繼承自Binder的Stub類笛钝,它實(shí)現(xiàn)了我們所需要的IDictionaryManager接口质况。我們先來(lái)看看它的幾個(gè)方法:

public static IDictionaryManager asInterface(IBinder obj)

這方法將一個(gè)IBinder對(duì)象轉(zhuǎn)換成實(shí)現(xiàn)了IDictionaryManager。如果請(qǐng)求調(diào)用的客戶端在同一進(jìn)程玻靡,就直接返回Stub對(duì)象本身结榄,如果不在同一進(jìn)程,就返回Stub類中的Proxy對(duì)象囤捻。Proxy類同樣實(shí)現(xiàn)了IDictionaryManager接口臼朗。

asBinder

這個(gè)方法很簡(jiǎn)單,就直接返回了Stub對(duì)象自己蝎土。

onTransact

這個(gè)方法主要是進(jìn)行用來(lái)調(diào)用其他函數(shù)的视哑,它會(huì)根據(jù)傳入的參數(shù)code,選擇調(diào)用add方法還是query方法瘟则。

接下來(lái)看看Stub.Proxy類中的方法

asBinder

這個(gè)方法返回了Proxy對(duì)象中的mRemote變量黎炉,mRemote變量的賦值發(fā)生在Proxy類的構(gòu)造函數(shù)中,而在Stub類的asInterface()方法中調(diào)用了該構(gòu)造函數(shù)Proxy()醋拧,并傳入了一個(gè)Binder對(duì)象慷嗜,將mRemote指向了這個(gè)Binder對(duì)象。

add

該add方法繼承自IDictionary接口丹壕。里面主要是將客戶端傳參寫入_data中,然后調(diào)用transact()方法庆械,在transact方法中,會(huì)回調(diào)Stub類中的onTransact()菌赖。

query

同上述add方法一樣缭乘,只不過(guò)多了一個(gè)返回值。

介紹完這些方法之后琉用,我們?cè)賮?lái)將這些方法串起來(lái)堕绩,講一下完整的IPC過(guò)程∫厥保客戶端連接遠(yuǎn)程Service時(shí)奴紧,Service會(huì)返回一個(gè)Binder對(duì)象,即我們?cè)贒ictionaryManagerService.onBind()中返回的繼承自Stub的匿名對(duì)象晶丘,該類實(shí)現(xiàn)了生詞本邏輯黍氮。
客戶端調(diào)用有兩種情況:

1:客戶端和服務(wù)端在同一個(gè)進(jìn)程

服務(wù)器端就直接將該Stub對(duì)象返回唐含,即我們?cè)趏nBind()中返回的Stub對(duì)象,客戶端調(diào)用返回對(duì)象的asInterface()方法沫浆,將Stub對(duì)象強(qiáng)轉(zhuǎn)為IDictionManager對(duì)象捷枯,直接調(diào)用IDictionManager對(duì)象中的方法,就像調(diào)用本地對(duì)象的方法一樣专执。

2:客戶端和服務(wù)端不在同一個(gè)進(jìn)程

因?yàn)槭强邕M(jìn)程淮捆,兩端的內(nèi)存資源是不能共享的,服務(wù)器端不可能將真正的Stub對(duì)象返回他炊,它只能將Stub中的代理類Prxoy對(duì)象返回争剿∫鸭瑁客戶端同樣調(diào)用返回對(duì)象的asInterface()方法痊末,將Proxy對(duì)象強(qiáng)轉(zhuǎn)為IDictionManager對(duì)象,所以這次調(diào)用IDictionManager中add方法的時(shí)候哩掺,調(diào)用的實(shí)際上是Prxoy.add()凿叠。這次調(diào)用發(fā)生在客戶端進(jìn)程,然后在Proxy.add()方法中嚼吞,將參數(shù)和目標(biāo)函數(shù)code寫入Binder驅(qū)動(dòng)中盒件,并通過(guò)回調(diào)服務(wù)端Stub.onTransact()方法,實(shí)現(xiàn)調(diào)用真正的Stub.add()方法舱禽。

C++層

我們開發(fā)時(shí)所見到的Binder是Android系統(tǒng)提供給我們的java接口炒刁,java層的Binder對(duì)象只是Android對(duì)底層Binder的一個(gè)封裝,提供給上層開發(fā)人員使用誊稚,真正的Binder其實(shí)隱藏在系統(tǒng)底層翔始,默默的替我們進(jìn)行著跨進(jìn)程通信。

Java層的服務(wù)端Binder對(duì)象在C++層對(duì)應(yīng)的對(duì)象為BBinder里伯,而客戶端拿到的BinderProxy對(duì)象對(duì)應(yīng)的則為BpBinder城瞎,BBindder和BpBinder都繼承自IBinder,上層Binder和BinderProxy之間的通信其實(shí)是BBinder和BpBinder之間的通信疾瓮。

Binder的核心是Binder Driver脖镀,即Binder驅(qū)動(dòng),雖然各個(gè)進(jìn)程不能共享自己的內(nèi)存空間狼电,但是系統(tǒng)的內(nèi)核空間是共享的蜒灰,每個(gè)進(jìn)程都可以訪問(wèn),而Binder Driver即存在于內(nèi)核空間肩碟,BBinder和BpBinder都是通過(guò)它來(lái)通信的强窖,所以可以把Binder驅(qū)動(dòng)當(dāng)作是BBinder和BpBinder之間的一座橋梁。當(dāng)然BBinder和BpBinder也不是直接和Binder驅(qū)動(dòng)打交道腾务,它們中間還隔著一個(gè)IPCThreadState毕骡。每一個(gè)進(jìn)程都有一個(gè)ProcessState對(duì)象,它負(fù)責(zé)打開Binder驅(qū)動(dòng),這樣該進(jìn)程中的所有線程都可以通過(guò)Binder驅(qū)動(dòng)通信未巫,而每個(gè)線程都會(huì)有一個(gè)IPCThreadState對(duì)象窿撬,它才是真正讀寫B(tài)inder驅(qū)動(dòng)的主角,它通過(guò)talkWithDriver()和驅(qū)動(dòng)打交道叙凡,將IPC數(shù)據(jù)寫入驅(qū)動(dòng)中劈伴。

Java-->C++對(duì)象轉(zhuǎn)換

回到我們上面的Demo,客戶端連接成功后拿到了BinderProxy對(duì)象握爷,那么這個(gè)服務(wù)端的Binder對(duì)象是如何轉(zhuǎn)為BinderPrxoy的呢跛璧?這點(diǎn)我們要看bindService的源碼,bindService流程很長(zhǎng)新啼,感興趣的讀者可以自己去看后者直接看老羅的分析追城,我這里直接告訴讀者大致過(guò)程:

1.客戶端請(qǐng)求bindService,先會(huì)請(qǐng)求ActivityManagerService;

2.ActvityManagerService再去找到對(duì)應(yīng)的Service燥撞,讓Service所在進(jìn)程創(chuàng)建并啟動(dòng)Service;

3.Service調(diào)用AMS.publishService()將Binder對(duì)象傳遞給AMS;

4.AMS拿到的Binder對(duì)象同樣為BinderProxy對(duì)象座柱,然后調(diào)用 c.conn.connected(r.name, service)方法,將BinderProxy對(duì)象傳遞給客戶端物舒。

這里要知道的是色洞,AMS也是處在一個(gè)單獨(dú)的進(jìn)程中,所以Binder對(duì)象不是直接返回給AMS冠胯,AMS也不是直接返回給客戶端的火诸,而是經(jīng)過(guò)了Binder驅(qū)動(dòng)。服務(wù)端Service將Binder對(duì)象傳遞給AMS時(shí)荠察,會(huì)調(diào)用AMS在服務(wù)端的代理對(duì)象ActivityManagerProxy.publishService()方法置蜀,準(zhǔn)確的說(shuō),Service端在此時(shí)成了AMS的客戶端割粮。

public void publishService(IBinder token,
        Intent intent, IBinder service) throws RemoteException {
    Parcel data = Parcel.obtain();
    Parcel reply = Parcel.obtain();
    data.writeInterfaceToken(IActivityManager.descriptor);
    data.writeStrongBinder(token);
    intent.writeToParcel(data, 0);
    data.writeStrongBinder(service);
    mRemote.transact(PUBLISH_SERVICE_TRANSACTION, data, reply, 0);
    reply.readException();
    data.recycle();
    reply.recycle();
}

該方法中通過(guò)Parcel.writeStrongBinder()盾碗,將服務(wù)端的Binder對(duì)象轉(zhuǎn)化成C++中的flat_binder_object對(duì)象,并將Binder對(duì)象的地址賦值給flat_binder_object對(duì)象中的cookie舀瓢。

struct flat_binder_object {
      unsigned long type;
      unsigned long flags;
      union {
              void *binder; /* local object */
              signed long handle; /* remote object */
      };
      void *cookie;
};

flat_binder_object對(duì)象最終被包裝在了data中廷雅,然后通過(guò)mRemote.transact(),將data數(shù)據(jù)傳送到IPCThreadState, IPCThreadState再次將data包裝成binder_transaction_data,并調(diào)用talkWithDriver(),將包裝好的數(shù)據(jù)傳遞給Binder驅(qū)動(dòng)京髓。

Binder驅(qū)動(dòng)收到數(shù)據(jù)后航缀,并不會(huì)急著將數(shù)據(jù)傳給AMS,因?yàn)閭魉偷臄?shù)據(jù)中有flat_binder_object堰怨,所以它會(huì)查詢flat_binder_object對(duì)應(yīng)的Binder對(duì)象在驅(qū)動(dòng)中是否有Binder節(jié)點(diǎn)芥玉,如果沒有,則會(huì)利用flat_binder_object中的cookie值(指向BBinder)創(chuàng)建一個(gè)Binder節(jié)點(diǎn)备图,并為該節(jié)點(diǎn)生成一個(gè)索引值灿巧,將索引值賦給flat_binder_object的handle赶袄。

然后AMS就可以取數(shù)據(jù)了,AMS同樣有一個(gè)IPCThreadState對(duì)象抠藕,Binder驅(qū)動(dòng)將數(shù)據(jù)傳遞給該對(duì)象饿肺,接收端拿到了數(shù)據(jù)后,會(huì)調(diào)用Parcel.readStrongBinder()盾似,這在java層是一個(gè)jni方法敬辣,該方法會(huì)先調(diào)用Native層的readStrongBinder(),利用flat_binder_object中的handle值創(chuàng)建BpBinder對(duì)象零院,然后該Native方法返回BpBinder的指針溉跃,jni方法再根據(jù)BpBinder指針創(chuàng)建java層的BinderProxy對(duì)象并返回給java層。至此BpBinder和BinderProxy對(duì)象都已經(jīng)創(chuàng)建完畢告抄。

BpBinder與BBinder的通信

上面我們已經(jīng)拿到了BpBinder撰茎,那BpBinder又將如何與BBinder通信呢?這里不得不提一個(gè)重要的變量:handle玄妈。還記得在創(chuàng)建BpBiner的時(shí)候乾吻,需要傳入flat_binder_object的handle嗎,而這個(gè)handle是Binder節(jié)點(diǎn)在驅(qū)動(dòng)中的索引拟蜻,即位置。這樣當(dāng)BpBinder通過(guò)transact()調(diào)用BBinder時(shí)枯饿,Binder驅(qū)動(dòng)就可以根據(jù)BpBinder提供的handle值找到Binder在驅(qū)動(dòng)中的節(jié)點(diǎn)酝锅,Binder節(jié)點(diǎn)保存了BBinder的地址,從而找到了BBinder奢方,實(shí)現(xiàn)對(duì)BBinder的調(diào)用搔扁。

系統(tǒng)服務(wù)的注冊(cè)

上面的例子展示的匿名Binder的通信,為什么說(shuō)是匿名蟋字,因?yàn)锽inder對(duì)象并沒有在ContextManager中實(shí)名注冊(cè)稿蹲。

ContextManager又是什么?它是系統(tǒng)服務(wù)的管理者鹊奖,它在java層和C++層的代理對(duì)象都為ServiceManager苛聘,系統(tǒng)服務(wù)可以通過(guò)ServiceManager.addService()將自己實(shí)名注冊(cè)到ContextManager中。為什么要實(shí)名注冊(cè)忠聚?如果不采用實(shí)名的方法设哗,系統(tǒng)服務(wù)那么多,你難道一個(gè)個(gè)的去記得它的handle两蟀?

ServiceManager.addService()會(huì)將服務(wù)的Binder對(duì)象傳到Binder Driver网梢,Binder Driver為其生成Binder節(jié)點(diǎn)后,ContextManager就會(huì)將服務(wù)的實(shí)名和其Binder節(jié)點(diǎn)的索引handle保存到自身赂毯。當(dāng)客戶端需要找一個(gè)系統(tǒng)服務(wù)時(shí)战虏,只需將服務(wù)名ServiceManager.getService()拣宰,ServiceManager就可以找到服務(wù)的索引handle,并創(chuàng)建對(duì)應(yīng)的BpBinder對(duì)象烦感,從而建立通信徐裸。比如java層的ActivityManagerService就是一個(gè)Binder類,我們就可以通過(guò)ServiceManager.getService("activity")啸盏,拿到它的代理對(duì)象重贺,從而進(jìn)行Activity生命周期的調(diào)度。

上面說(shuō)到ServiceManager是ContextManager的代理回懦,因?yàn)镃ontextManager本身也就是一個(gè)服務(wù)气笙,服務(wù)端和客戶端想調(diào)用它的addService或getService時(shí),也必須通過(guò)ServiceManager來(lái)跨進(jìn)程怯晕∏逼裕可是我該怎么拿到這個(gè)代理呢?我總不能調(diào)用它的getService來(lái)獲取它自己吧舟茶。Binder這一點(diǎn)設(shè)計(jì)的很奇妙谭期,ContextManager在BinderDriver中的節(jié)點(diǎn)索引為0,誰(shuí)讓它是老大呢吧凉。這樣大家都知道了隧出,我想調(diào)用ContextManger,直接根據(jù)handle=0就可以生成它的代理對(duì)象BpBinder了阀捅,從而創(chuàng)建代理對(duì)象ServiceManager胀瞪。

總結(jié)

系統(tǒng)中每個(gè)app和系統(tǒng)服務(wù)都活在自己的進(jìn)程中,無(wú)法訪問(wèn)彼此的內(nèi)存空間饲鄙,正是因?yàn)橄嗷ネㄐ诺膹?qiáng)烈需求凄诞,從而誕生了Binder。Binder驅(qū)動(dòng)就像所有進(jìn)程共同的地盤忍级,系統(tǒng)服務(wù)可以這里留下自己的地址帆谍,而客戶端可以可以根據(jù)這些地址找到對(duì)應(yīng)的服務(wù),相互通信轴咱,從而千里姻緣一線牽汛蝙,百年恩愛雙心結(jié)。

(轉(zhuǎn)載請(qǐng)注明ID:半棧工程師嗦玖,歡迎訪問(wèn)個(gè)人博客https://halfstackdeveloper.github.io/)

歡迎關(guān)注我的知乎專欄:https://zhuanlan.zhihu.com/halfstack

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末患雇,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子宇挫,更是在濱河造成了極大的恐慌苛吱,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,406評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件器瘪,死亡現(xiàn)場(chǎng)離奇詭異翠储,居然都是意外死亡绘雁,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門援所,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)庐舟,“玉大人,你說(shuō)我怎么就攤上這事住拭∨猜裕” “怎么了?”我有些...
    開封第一講書人閱讀 163,711評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵滔岳,是天一觀的道長(zhǎng)杠娱。 經(jīng)常有香客問(wèn)我,道長(zhǎng)谱煤,這世上最難降的妖魔是什么摊求? 我笑而不...
    開封第一講書人閱讀 58,380評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮刘离,結(jié)果婚禮上室叉,老公的妹妹穿的比我還像新娘。我一直安慰自己硫惕,他們只是感情好茧痕,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著疲憋,像睡著了一般凿渊。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上缚柳,一...
    開封第一講書人閱讀 51,301評(píng)論 1 301
  • 那天,我揣著相機(jī)與錄音搪锣,去河邊找鬼秋忙。 笑死,一個(gè)胖子當(dāng)著我的面吹牛构舟,可吹牛的內(nèi)容都是我干的灰追。 我是一名探鬼主播,決...
    沈念sama閱讀 40,145評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼狗超,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼弹澎!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起努咐,我...
    開封第一講書人閱讀 39,008評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤苦蒿,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后渗稍,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體佩迟,經(jīng)...
    沈念sama閱讀 45,443評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡团滥,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了报强。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片灸姊。...
    茶點(diǎn)故事閱讀 39,795評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖秉溉,靈堂內(nèi)的尸體忽然破棺而出力惯,到底是詐尸還是另有隱情,我是刑警寧澤召嘶,帶...
    沈念sama閱讀 35,501評(píng)論 5 345
  • 正文 年R本政府宣布父晶,位于F島的核電站,受9級(jí)特大地震影響苍蔬,放射性物質(zhì)發(fā)生泄漏诱建。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評(píng)論 3 328
  • 文/蒙蒙 一碟绑、第九天 我趴在偏房一處隱蔽的房頂上張望俺猿。 院中可真熱鬧,春花似錦格仲、人聲如沸押袍。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)谊惭。三九已至,卻和暖如春侮东,著一層夾襖步出監(jiān)牢的瞬間圈盔,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工悄雅, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留驱敲,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,899評(píng)論 2 370
  • 正文 我出身青樓宽闲,卻偏偏與公主長(zhǎng)得像众眨,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子容诬,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評(píng)論 2 354

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