Binder簡析

1.Binder簡介

我們知道,在Android操作系統(tǒng)中,每個進程都擁有屬于自己的獨立虛擬地址空間(用戶空間+內(nèi)核空間)。一般情況下,進程的用戶空間是各自獨立的,無法進行數(shù)據(jù)共享;而內(nèi)核空間卻是所有進程都可以共同訪問的。


進程空間

對于Android系統(tǒng)來說,其特有的IPC機制是Binder。正如其名字所喻(粘粘劑),通過Binder作為橋梁,將系統(tǒng)各個進程組件進行連接。

2.Binder優(yōu)點

1.Binder更加簡潔和快速,消耗的內(nèi)存資源更小。
2.傳統(tǒng)的進程間通信可能會增加進程的開銷,而且有進程過載和安全漏洞等方面的風險,Binder正好能解決和避免這些問題。
3.用驅動程序來推進進程間的通信。
4.通過共享內(nèi)存來提高性能。
5.為進程請求分配每個進程的線程池。
6.針對系統(tǒng)中的對象引入了引用計數(shù)和跨進程的對象引用映射。
7.進程間同步調(diào)用。

3.Binder實現(xiàn)IPC通訊原理

3.1 Binder IPC基礎組件

Binder通訊采用C/S架構,其進行IPC涉及到的組件主要包含Client,Server,ServerManager和binder驅動。

Client:客戶端
Server:服務端
ServerManager:用于管理系統(tǒng)中的各種服務。提供服務注冊功能和查詢服務功能。(此處的ServerManager是Native層的ServiceManager(c++),不是framework層的ServerManager(java))。
binder驅動:位于內(nèi)核空間,提供Client,Server,ServerManager跨進程通訊功能。

3.2實例講解

我們知道,Android提供的AIDL通訊底層采用的就是Binder機制。現(xiàn)在我們采用AIDL方式創(chuàng)建2個應用進行進程間通訊。
(1)首先定義aidl接口:

// IMath.aidl
package com.yn.bindertest.aidl;

// Declare any non-default types here with import statements

interface IMath {
    int add(in int a,in int b);
    void result(in int justTestWithoutReturnValue);
}

(2)服務端代碼編寫:

public class Server extends Service {
    private static final String TAG = "ynServer";
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return new IMath.Stub(){
            @Override
            public int add(int a, int b) throws RemoteException {
                Log.d(TAG, "add: "+Thread.currentThread().getName());
                return a+b;
            }

            @Override
            public void result(int justTestWithoutReturnValue) throws RemoteException {
                Log.d(TAG, "result: "+Thread.currentThread().getName());
            }
        };
    }
}

(3)客戶端代碼編寫:

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "ynClient";
    private TextView tvShow;
    private IMath mServer;
    private ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.d(TAG, "onServiceConnected: "+Thread.currentThread().getName());
            mServer = IMath.Stub.asInterface(service);
            try {
                mServer.asBinder().linkToDeath(mDeathRecipient,0);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.d(TAG, "onServiceDisconnected: "+Thread.currentThread().getName());
        }
    };
    private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient(){

        @Override
        public void binderDied() {
            Log.d(TAG, "binderDied: "+Thread.currentThread().getName());
            bindService(new Intent(MainActivity.this, Server.class),mServiceConnection , Context.BIND_AUTO_CREATE);
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        tvShow = (TextView)findViewById(R.id.tvShow);
        bindService(new Intent(this, Server.class),mServiceConnection , Context.BIND_AUTO_CREATE);


    }

    public void onClick(View view)
    {
        try {
            int sum = mServer.add(10,30);
            Log.d(TAG, "onClick: sum="+sum);
            tvShow.setText(sum+"");
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }
}

(4)記得在AndroidManifest.xml中將Service設置到另一個進程中。

 <service
            android:name=".Server"
            android:process="com.yn.bindertest.server" />

通過以上步驟,我們就實現(xiàn)了使用AIDL進行跨進程通訊功能,結果如下:

AIDL跨進程通訊

當程序啟動時,bindService()成功時就會回調(diào)onServiceConnected,然后手動調(diào)用add可以看到add過程是運行在Server端的binder線程池中的;由于為Server設置了死亡代理,當服務端異常被kill時,會回調(diào)死亡代理IBinder.DeathRecipient.binderDied和ServiceConnection.onServiceDisconnected,兩者的區(qū)別就是binderDied是運行在客戶端的binder線程池中的,而onServiceDisconnected是運行在主線程中的
更關鍵的一點是:當客戶端調(diào)用遠程函數(shù)時,客戶端進程會被掛起,等待遠程服務端執(zhí)行完成后才重新運行客戶端進程,因此,這是一個同步調(diào)用,應小心因為服務端的耗時方法導致客戶端發(fā)生ANR。

3.3 aidl的binder機制講解

當我們創(chuàng)建完成IMath.aidl文件后,rebuild一下,可以看到IDE為我們生成了一個.java文件,路徑為:app\build\generated\source\aidl\debug\com\yn\bindertest\aidl\IMath.java
該IMath.java的具體內(nèi)容如下:


IMath.java

可以看到,IDE為我們自動生成的其實就是一個繼承了IInterface的接口,生成的接口類名和方法與我們aidl中聲明的完全一致,只是另外增加了一個Stub內(nèi)部抽象靜態(tài)類,而這個Stub類繼承了Binder并且實現(xiàn)了我們這個IMath(由于IMath繼承了IInterface,而IInterface只有一個接口方法asBinder),所以這個Stub類就是一個Binder,并且必須實現(xiàn)我們自己定義的IMath中的接口方法,并提供asBinder方法給到系統(tǒng)調(diào)用。

所以這個Stub類才是我們要著重進行研究的,那么這個Stub類的具體如下所示:

    /**
     * Local-side IPC implementation stub class.
     */
    public static abstract class Stub extends android.os.Binder implements com.yn.bindertest.aidl.IMath {
        private static final java.lang.String DESCRIPTOR = "com.yn.bindertest.aidl.IMath";

        /**
         * Construct the stub at attach it to the interface.
         */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        /**
         * Cast an IBinder object into an com.yn.bindertest.aidl.IMath interface,
         * generating a proxy if needed.
         */
        public static com.yn.bindertest.aidl.IMath asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.yn.bindertest.aidl.IMath))) {
                return ((com.yn.bindertest.aidl.IMath) iin);
            }
            return new com.yn.bindertest.aidl.IMath.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);
                    int _arg0;
                    _arg0 = data.readInt();
                    int _arg1;
                    _arg1 = data.readInt();
                    int _result = this.add(_arg0, _arg1);
                    reply.writeNoException();
                    reply.writeInt(_result);
                    return true;
                }
                case TRANSACTION_result: {
                    data.enforceInterface(DESCRIPTOR);
                    int _arg0;
                    _arg0 = data.readInt();
                    this.result(_arg0);
                    reply.writeNoException();
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements com.yn.bindertest.aidl.IMath {
            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 add(int a, int b) 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.writeInt(a);
                    _data.writeInt(b);
                    mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readInt();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

            @Override
            public void result(int justTestWithoutReturnValue) 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.writeInt(justTestWithoutReturnValue);
                    mRemote.transact(Stub.TRANSACTION_result, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
        }

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

看一下asInterface()內(nèi)容:

asInterface

可以看出,通過DESCRIPTOR(即包名+類名:"com.yn.bindertest.aidl.IMath")先進行本地查找,如果找到,那么返回本地binder類對象(即onBind返回的那個binder對象),如果找不到,那么返回一個binder代理類,這個代理類具體內(nèi)容如下:

       private static class Proxy implements com.yn.bindertest.aidl.IMath {
            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 add(int a, int b) 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.writeInt(a);
                    _data.writeInt(b);
                    mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readInt();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

            @Override
            public void result(int justTestWithoutReturnValue) 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.writeInt(justTestWithoutReturnValue);
                    mRemote.transact(Stub.TRANSACTION_result, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
        }

可以看到,這個Proxy也實現(xiàn)了IMath接口,并且持有一個遠程服務的IBinder對象(其實這個IBinder是一個BinderProxy對象,是bindservice時,向ServiceManager獲取得到的)。所以,當處于不同的進程時,客戶端得到的是一個Proxy代理類,然后由于該代理類實現(xiàn)了aidl定義的接口,并且持有一個遠程IBinder接口,所以在調(diào)用aidl接口方法時,可以看到,它通過將函數(shù)參數(shù)放入到一個Parcel(_data)中,提供一個Parcel(_reply)存儲返回結果(如果有返回的話),然后通過持有的遠程IBinder對象的transact將函數(shù)標識,參數(shù)Parcel和返回結果Parcel發(fā)送給遠程服務,客戶端調(diào)用transact時,客戶端進程被掛起,然后通過binder驅動調(diào)用到遠程服務的onTransact函數(shù),通過傳遞過來的函數(shù)標識,就可以知道要執(zhí)行哪個函數(shù),然后取出對應的函數(shù)參數(shù),運行完成后,將返回結果存儲到_reply中,通過binder驅動返回給客戶端,客戶端只要讀取_reply就可以獲取到結果。這樣,就完成了一次IPC通訊。

綜上所述,通過aidl實現(xiàn)IPC的原理如下:
1.IDE根據(jù)我們定義的aidl生成一個同名接口,并繼承IInterface,目的是為了客戶端可以通過asBinder獲取到binder對象(同個進程則獲取的是同一個binder,不同進程獲取到的是一個BinderProxy代理對象)。
2.當Server(Service)啟動時,它會通過binder驅動向ServiceManager進行注冊(binder標識+binder引用);當Client要獲取遠程服務(bindservice)時,它首先要通過binder驅動向ServiceManger獲取特定遠程服務的binder引用,獲取成功后,就可以對遠程服務進行IPC通訊(也是通過binder驅動)。

總結

應用間通過Binder實現(xiàn)IPC的整體過程如下圖所示:

binder綜合流程

1.當Server啟動時,它會通過進程內(nèi)的0號binder引用,將服務名和binder傳遞給內(nèi)核0號binder引用,再依次傳給ServiceManager內(nèi)核binder實體,最后傳入ServiceManager的進程binder中。經(jīng)過這個過程后,ServiceManager內(nèi)部就記錄了Server相應的信息,該過程即是Server向ServiceManager的注冊過程。

2.當Client要進行遠程服務通訊時,它首先會經(jīng)由進程內(nèi)0號binder引用通知到內(nèi)核0號binder引用,然后經(jīng)由ServiceManager內(nèi)核binder實體,最后將所需的服務信息傳入到ServiceManager進程的binder實體中,那么ServiceManager通過查詢相關服務信息,如果存在對應服務,就會通過binder驅動傳遞一個擁有訪問遠程服務能力的BinderProxy給到Client(即bindservice時,onServiceConnection中的onConnected中的IBinder參數(shù))。這個過程就是Client的獲取遠程服務過程。

3.Client獲取遠程服務成功后,就可以通過ServiceManager傳遞過來的BinderProxy對象創(chuàng)建出一個遠程服務Binder的代理類對象(asInterface),通過這個代理類對象,Client就可以執(zhí)行遠程服務相關方法了,從而實現(xiàn)跨進程IPC通訊。

4.當Client調(diào)用遠程方法時,本地遠程binder的代理類對象會調(diào)用其持有的BinderProxy對象的transact方法,這個方法會將要調(diào)用的函數(shù)標識,參數(shù)和返回值(如果有的話)放入到Client的共享內(nèi)存中,然后經(jīng)由內(nèi)核binder驅動運輸?shù)较鄳h程服務的共享內(nèi)存中,然后遠程服務的進程binder實體的onTransact函數(shù)會被調(diào)用,這個函數(shù)的功能就是從共享內(nèi)存中獲取傳遞過來的相關信息,通過函數(shù)標識就知道要運行哪個函數(shù),然后再將相關的參數(shù)提取出來(如果有的話),執(zhí)行完畢后,就將返回結果存入之前傳遞過來的容器(Parcel)內(nèi),最后經(jīng)由內(nèi)核binder驅動傳遞回給到Client中。這樣,一次完整的binder IPC通訊就完成了。

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

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

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