Android源碼解析RPC系列(一)---Binder原理

看了幾天的Binder,決定有必要寫一篇博客,記錄一下學習成果,Binder是Android中比較綜合的一塊知識了,目前的理解只限於JAVA層。首先Binder是幹嘛用的?不用說,跨進程通信全靠它,操作系統的不同進程之間,數據不共享,對於每個進程來說,它都天真地以爲自己獨享了整個系統,完全不知道其他進程的存在,進程之間需要通信需要某種系統機制才能完成,在Android整個系統架構中,採用了大量的C/S架構的思想,所以Binder的作用就顯得非常重要了,但是這種機制爲什麼是Binder呢?在Linux中的RPC方式有管道,消息隊列,共享內存等,消息隊列和管道採用存儲-轉發方式,即數據先從發送方緩存區拷貝到內核開闢的緩存區中,然後再從內核緩存區拷貝到接收方緩存區,這樣就有兩次拷貝過程。共享內存不需要拷貝,但控制複雜,難以使用。Binder是個折中的方案,只需要拷貝一次就行了。其次Binder的安全性比較好,好在哪裏,在下還不是很清楚,基於安全性和傳輸的效率考慮,選擇了Binder。Binder的英文意思是粘結劑,Binder對象是一個可以跨進程引用的對象,它的實體位於一個進程中,這個進程一般是Server端,該對象提供了一套方法用以實現對服務的請求,而它的引用卻遍佈於系統的各個進程(Client端)之中,這樣Client通過Binder的引用訪問Server,所以說,Binder就像膠水一樣,把系統各個進程粘結在一起了,廢話確實有點多。

一、概念理解

爲了從而保障了系統的安全和穩定,整個系統被劃分成內核空間和用戶空間
內核空間:獨立於普通的應用程序,可以訪問受保護的內存空間,有訪問底層硬件設備的所有權限。
用戶空間:相對與內核空間,上層運用程序所運行的空間就是用戶空間,用戶空間訪問內核空間的唯一方式就是系統調用。一個4G的虛擬地址空間,其中3G是用戶空間,剩餘的1G是內核空間。如果一個用戶空間想與另外一個用戶空間進行通信,就需要內核模塊支持,這個運行在內核空間的,負責各個用戶進程通過Binder通信的內核模塊叫做Binder驅動,雖然叫做Binder驅動,但是和硬件並沒有什麼關係,只是實現方式和設備驅動程序是一樣的,提供了一些標準文件操作。

Paste_Image.png

二、Binder的通信模型

在寫AIDL的時候,一般情況下,我們有兩個進程,一個作爲Server端提供某種服務,然後另外一個進程作爲Client端,連接Server端之後,就 可以使用Server裏面定義的服務。這種思想是一種典型的C/S的思想。值得注意的是Android系統中的Binder自身也是C/S的架構,也有Server端與Client端。一個大的C/S架構中,也有一個小的C/S架構。

先籠統的說一下,在整個Binder框架中,由系列組件組成,分別是Client、Server、ServiceManager和Binder驅動程序,其中Client、Server和ServiceManager運行在用戶空間,Binder驅動程序運行內核空間。運行在用戶空間中的Client、Server和ServiceManager,是在三個不同進程中的,Server進程中中定義了服務提供給Client進程使用,並且Server中有一個Binder實體,但是Server中定義的服務並不能直接被Client使用,它需要向ServiceManager註冊,然後Client要用服務的時候,直接向ServerManager要,ServerManager返回一個Binder的替身(引用)給Client,這樣Client就可以調用Server中的服務了。

場景:進程A要調用進程B裏面的一個draw方法處理圖片。

分析:在這種場景下,進程A作爲Client端,進程B做爲Server端,但是A/B不在同一個進程中,怎麼來調用B進程的draw方法呢,首先進程B作爲Server端創建了Binder實體,爲其取一個字符形式,可讀易記的名字,並將這個Binder連同名字以數據包的形式通過Binder驅動發送給ServiceManager,也就是向ServiceManager註冊的過程,告訴ServiceManager,我是進程B,擁有圖像處理的功能,ServiceManager從數據包中取出名字和引用以一個註冊表的形式保留了Server進程的註冊信息。爲什麼是以數據包的形式呢,因爲這是兩個進程,直接傳遞對象是不行滴,只能是一些描述信息。現在Client端進程A聯繫ServiceManager,說現在我需要進程B中圖像處理的功能,ServiceManager從註冊表中查到了這個Binder實體,但是呢,它並不是直接把這個Binder實體直接給Client,而是給了一個Binder實體的代理,或者說是引用,Client通過Binder的引用訪問Server。分析到現在,有個關鍵的問題需要說一下,ServiceManager是一個進程,Server是另一個進程,Server向ServiceManager註冊Binder必然會涉及進程間通信。當前實現的是進程間通信卻又要用到進程間通信,這就好象蛋可以孵出雞前提卻是要找只雞來孵蛋,確實是這樣的,ServiceManager中預先有了一個自己的Binder對象(實體),就是那隻雞,然後Server有個Binder對象的引用,就是那個蛋,Server需要通過這個Binder的引用來實現Binder的註冊。雞就一隻,蛋有很多,ServiceManager進程的Binder對象(實體)僅有一個,其他進程所擁有的全部都是它的代理。同樣一個Server端Binder實體也應該只有一個,對應所有Client端全部都是它的代理。

我們再次理解一下Binder是什麼?在Binder通信模型的四個角色裏面;他們的代表都是“Binder”,一個Binder對象就代表了所有,包括了Server,Client,ServerManger,這樣,對於Binder通信的使用者而言,不用關心實現的細節。對Server來說,Binder指的是Binder實體,或者說是本地對象,對於Client來說,Binder指的是Binder代理對象,也就是Binder的引用。對於Binder驅動而言,在Binder對象進行跨進程傳遞的時候,Binder驅動會自動完成這兩種類型的轉換。

簡單的總結一下,通過上面一大段的分析,一個Server在使用的時候需要經歷三個階段
- 服務註冊 Server端與SystemManager之間的IPC
- 服務檢索 Client端與SystemManager之間的IPC
- 服務使用 Client端與Server端之間的IPC

三、AIDL過程分析

1、定義一個AIDL文件
Game.aidl

package test.wangjing.com.aidl.domain;
parcelable Game;

GameManager .aidl

package test.wangjing.com.aidl;

import test.wangjing.com.aidl.domain.Game;

interface GameManager {
          Game querryGameById(int pGameId);
}

2、定義遠端服務Service
在遠程服務中的onBind方法,實現AIDL接口的具體方法,並且返回Binder對象

    @Override
    public IBinder onBind(Intent intent) {
        return mGameManager;
    }

    GameManager.Stub mGameManager = new GameManager.Stub() {
        @Override
        public IBinder asBinder() {
            return null;
        }

        @Override
        public Game querryGameById(int pGameId) throws RemoteException {
            return mGameMap.get(pGameId);
        }
    };

3、本地創建連接對象

 private class GameServiceConnection implements ServiceConnection {

        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {

            mGameManager = (GameManager.Stub) GameManager.Stub.asInterface(iBinder);
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {

        }
    }
public void actionService(View view) {
        Intent intent = new Intent(this, GameService.class);
        bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
    }

以上就是一個遠端服務的一般套路,如果是在兩個進程中,就可以進程通信了,現在我們分析一下,這個通信的流程。重點是GameManager這個編譯生成的類。

public interface GameManager extends android.os.IInterface {
    /**
     * Local-side IPC implementation stub class.
     */
    public static abstract class Stub extends android.os.Binder implements test.wangjing.com.aidl.GameManager {
        private static final java.lang.String DESCRIPTOR = "test.wangjing.com.aidl.GameManager";

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

        /**
         * Cast an IBinder object into an test.wangjing.com.aidl.GameManager interface,
         * generating a proxy if needed.
         */
        public static test.wangjing.com.aidl.GameManager asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof test.wangjing.com.aidl.GameManager))) {
                return ((test.wangjing.com.aidl.GameManager) iin);
            }
            return new test.wangjing.com.aidl.GameManager.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_querryGameById: {
                    data.enforceInterface(DESCRIPTOR);
                    int _arg0;
                    _arg0 = data.readInt();
                    test.wangjing.com.aidl.domain.Game _result = this.querryGameById(_arg0);
                    reply.writeNoException();
                    if ((_result != null)) {
                        reply.writeInt(1);
                        _result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
                    } else {
                        reply.writeInt(0);
                    }
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements test.wangjing.com.aidl.GameManager {
            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 test.wangjing.com.aidl.domain.Game querryGameById(int pGameId) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                test.wangjing.com.aidl.domain.Game _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeInt(pGameId);
                    mRemote.transact(Stub.TRANSACTION_querryGameById, _data, _reply, 0);
                    _reply.readException();
                    if ((0 != _reply.readInt())) {
                        _result = test.wangjing.com.aidl.domain.Game.CREATOR.createFromParcel(_reply);
                    } else {
                        _result = null;
                    }
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }

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

    public test.wangjing.com.aidl.domain.Game querryGameById(int pGameId) throws android.os.RemoteException;
}

從類的關係來看,首先接口GameManager 繼承 IInterface ,IInterface是一個接口,在GameManager內部有一個內部類Stub,Stub繼承了Binder,(Binder實現了IBinder),並且實現了GameManager接口,在Stub中還有一個內部類Proxy,Proxy也實現了GameManager接口,一個整體的結構是這樣的

現在的問題是,Stub是什麼?Proxy又是什麼?在上面說了在Binder通信模型的四個角色裏面;他們的代表都是“Binder”,一個Binder對象就代表了所有,包括了Server,Clinet,ServerManger,爲了兩個進程的通信,系統給予的內核支持是Binder,在抽象一點的說,Binder是系統開闢的一塊內存空間,兩個進程往這塊空間裏面讀寫數據就行了,Stub從Binder中讀數據,Proxy向Binder中寫數據,達到進程間通信的目的。首先我們分析Stub。

public static abstract class Stub extends android.os.Binder implements test.wangjing.com.aidl.GameManager

Stub 類繼承了Binder ,說明了Stub有了跨進程傳輸的能力,實現了GameManager接口,說明它有了根據遊戲ID查詢一個遊戲的能力。我們在bind一個Service之後,在onServiceConnecttion的回調裏面,就是通過asInterface方法拿到一個遠程的service的。

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

asInterface調用queryLocalInterface。

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

mDescriptor,mOwner其實是Binder的成員變量,Stub繼承了Binder,在構造函數的時候,對着兩個變量賦的值。

 public Stub() {
      this.attachInterface(this, DESCRIPTOR);
   }
public void attachInterface(IInterface owner, String descriptor) {
        mOwner = owner;
        mDescriptor = descriptor;
    }

如果客戶端和服務端是在一個進程中,那麼其實queryLocalInterface獲取的就是Stub對象,如果不在一個進程queryLocalInterface查詢的對象肯定爲null,因爲不同進程有不同虛擬機,肯定查不到mOwner對象的,所以這時候其實是返回的Proxy對象了。拿到Stub對象後,通常在onServiceConnected中,就把這個對象轉換成我們多定義AIDL接口。

private class GameServiceConnection implements ServiceConnection {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            mGameManager = (GameManager.Stub) GameManager.Stub.asInterface(iBinder);
        }
        @Override
        public void onServiceDisconnected(ComponentName componentName) {

        }
    }

比如我們這裏會轉換成GameManager,有了GameManager對象,就可以調用後querryGameById方法了。如果是一個進程,那直接調用的是自己的querryGameById方法,如果不是一個進程,那調用了就是代理的querryGameById方法了。

private static class Proxy implements test.wangjing.com.aidl.GameManager {
            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 test.wangjing.com.aidl.domain.Game querryGameById(int pGameId) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                test.wangjing.com.aidl.domain.Game _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeInt(pGameId);
                    mRemote.transact(Stub.TRANSACTION_querryGameById, _data, _reply, 0);
                    _reply.readException();
                    if ((0 != _reply.readInt())) {
                        _result = test.wangjing.com.aidl.domain.Game.CREATOR.createFromParcel(_reply);
                    } else {
                        _result = null;
                    }
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }

看到其中關鍵的一行是

        mRemote.transact(Stub.TRANSACTION_querryGameById, _data, _reply, 0);

mRemote就是一個IBinder對象,相對於Stub,Proxy 是組合關係(HAS-A),內部有一個IBinder對象mRemote,Stub是繼承關係(IS-A),直接實現了IBinder接口。

public native boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException;

transact是個native方法,最終還會回掉JAVA層的onTransact方法。

@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_querryGameById: {
                    data.enforceInterface(DESCRIPTOR);
                    int _arg0;
                    _arg0 = data.readInt();
                    test.wangjing.com.aidl.domain.Game _result = this.querryGameById(_arg0);
                    reply.writeNoException();
                    if ((_result != null)) {
                        reply.writeInt(1);
                        _result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
                    } else {
                        reply.writeInt(0);
                    }
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

onTransact根據調用號(每個AIDL函數都有一個編號,在跨進程的時候,不會傳遞函數,而是傳遞編號指明調用哪個函數)調用相關函數;在這個例子裏面,調用了Binder本地對象的querryGameById方法;這個方法將結果返回給驅動,驅動喚醒掛起的Client進程裏面的線程並將結果返回。於是一次跨進程調用就完成了。

Please accept mybest wishes for your happiness and success !

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章