Binder進程間通訊詳解

前言

隔行如隔山,這句話一樣適用於任什麼時候候,即時同一個專業,深刻下去的話,差異也是巨大的。今天,講下本身如何學習binder進程間通訊的機制的一些看法。開始的時候,只知道 Binder 是個很底層的東西,甚至對於具體是什麼用都不是很清楚。java

主要是經過兩種方式:android

  • 看別人寫的Binder博文
    目的很簡單,大概瞭解Binder是個什麼東西,有哪些核心的東西,對於看源碼的時候選擇性過濾有幫助,最好是看了後畫下思惟導圖總結下或者能夠畫下流程圖。c++

  • 看Binder源碼
    對於切入點的話,從最熟悉的客戶端入手;選擇典型的具體例子,分析下前面從他人那邊看到的重點。git

Binder世界之門

  • 學習如何使用東西和思考爲何要創造東西是徹底不同的,不少人寫文章每每是忽略了後者。

想下若是兩個進程須要相互通訊,須要作什麼? 假設其中一個進程是客戶端進程,另外一個是服務端進程,這裏約定簡稱客戶端與服務端。數組

  • 1.客戶端須要知道哪個是他要調用的服務端的方法。
  • 2.客戶端如何傳遞和接收數據給服務端。
  • 3.屏蔽底層通訊的細節,包括數據交換經過共享內存。

第一個問題很簡單,搞一個惟一的標識符,經過包名+類名。
第二個問題,可使用實現Parcelable接口的類,why? 這是由於 Android 系統可經過它將對象分解成可編組到各進程的原語。
第三個問題,封裝一個類來具體的實現,他的名字叫Binder,而後這個binder的話,須要服務端來繼承。緩存

Binder調用流程圖

具體看源碼

找好切入點

  • 因爲binder是支撐Android系統的重要組成部分,binder從源碼來講是很龐大的,因此這裏找一個好的切入點變得很是重要。正所謂,前人栽樹,後人乘涼,參考 完全理解Android Binder通訊架構 此文,切入點是startService開始的,具體分析的是客戶端進程調用服務端進程的過程。

[=> ActivityManagerNative::ActivityManagerProxy]架構

public ComponentName startService(IApplicationThread caller, Intent service,
            String resolvedType, int userId) throws RemoteException
    {
        //這一步操做主要實例了兩個Parcel對象,datat是客戶端的發送數據,reply是服務端返回的數據 
        //具體參考:Parcel的obtain和recycle方法
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        data.writeInterfaceToken(IActivityManager.descriptor);
        data.writeStrongBinder(caller != null ? caller.asBinder() : null);
        service.writeToParcel(data, 0);
        data.writeString(resolvedType);
        data.writeInt(userId);
        //
        mRemote.transact(START_SERVICE_TRANSACTION, data, reply, 0);
        reply.readException();
        ComponentName res = ComponentName.readFromParcel(reply);
        //具體參考:Parcel的obtain和recycle方法
        data.recycle();
        reply.recycle();
        return res;
    }
Parcel的obtain和recycle方法
  • 這兩個方法看名字很熟悉有沒有?前次講的handler中生成的Message其實也有這兩個方法,知覺告訴我,這兩處地方的原理是同樣的,心動不如行動,立刻進入驗證環節。
obtain方法

[=> Parcel.java::obtain]app

private static final int POOL_SIZE = 6;
    private static final Parcel[] sOwnedPool = new Parcel[POOL_SIZE];
    ...
    ...
    ...
  /**
     *說明下:這個pool是上面聲明的一個6容量的Parcel數組,方法中省略若干代碼
     *從這個pool中檢索一個新的 Parcel 對象
     */
    public static Parcel obtain() {
        final Parcel[] pool = sOwnedPool;
        synchronized (pool) {
            Parcel p;
            for (int i=0; i<POOL_SIZE; i++) {
                p = pool[i];
                if (p != null) {
                    pool[i] = null;
                    return p;
                }
            }
        }
        //這裏參數傳的是0
        return new Parcel(0);
    }

    ...
    ...
    ...
    private Parcel(int nativePtr) {
        init(nativePtr);
    }

    private void init(int nativePtr) {
        if (nativePtr != 0) {
            mNativePtr = nativePtr;
            mOwnsNativeParcelObject = false;
        } else {
            //nativeCreate是個native方法,參考:nativeCreate方法,mNativePtr這個是能夠理解成指針
            mNativePtr = nativeCreate();
            mOwnsNativeParcelObject = true;
        }
    }
    
    private void freeBuffer() {
        if (mOwnsNativeParcelObject) {
            nativeFreeBuffer(mNativePtr);
        }
    }
  • obtain方法:若是緩存Parcel數組不爲空則使用緩存數組,不然自家建立一個Parcel。
nativeCreate方法

[=> android_os_Parcel.cpp::android_os_Parcel_create]學習

/**
*這裏來看的話,很清楚的看到返回的是parcel的指針
**/
static jint android_os_Parcel_create(JNIEnv* env, jclass clazz)
{
    Parcel* parcel = new Parcel();
    return reinterpret_cast<jint>(parcel);
}
recycle方法

[=> Parcel.java::recycle]this

/**
     * 把一個Parcel對象放回到pool池中。在這回調以前,不須要這個對象。You must not touch
     * the object after this call.
     */
    public final void recycle() {
        //這個方法內部調的是native方法,實際上是根據指針釋放內存
        freeBuffer();

        final Parcel[] pool;
        //這裏實際上是在obtain無參方法中實例化建立過程當中賦值爲true
        if (mOwnsNativeParcelObject) {
            pool = sOwnedPool;
        } else {
            //mNativePtr能夠看作是指向Parcel對象的指針,sHolderPool也是一個6容量的Parcel數組 
            mNativePtr = 0;
            pool = sHolderPool;
        }
        //放到緩存數組中
        synchronized (pool) {
            for (int i=0; i<POOL_SIZE; i++) {
                if (pool[i] == null) {
                    pool[i] = this;
                    return;
                }
            }
        }
    }
   ...
   ...
   ...
    
    private void freeBuffer() {
        if (mOwnsNativeParcelObject) {
            nativeFreeBuffer(mNativePtr);
        }
    }
  • recycle方法:根據mOwnsNativeParcelObject的值,若爲true,則不帶參數的obtain方法獲取的對象,把其放入sOwnedPool中,不然帶nativePtr的obtain(int obj)方法獲取nativePtr指向的對象,把其放入sHolderPool中。

看了源碼後發現,其實這個原理相似的,緩存的方式有區別,Message是經過鏈表方式來進行,Parcel是經過固定的數組,殊途同歸之妙。

探祕mRemote

涉及代碼:須要解決兩個問題,一個是mRemote哪裏來,另外一個是mRemote的transact方法

mRemote.transact(START_SERVICE_TRANSACTION, data, reply, 0);

mRemote哪裏來

  • 搜尋發現,ActivityManagerProxy 類的構造方法進行賦值,而 ActivityManagerProxy(AMP) 這個類又被 asInterface 方法調用。

[-> ActivityManagerNative.java::asInterface]

public abstract class ActivityManagerNative extends Binder implements IActivityManager {
    static public IActivityManager asInterface(IBinder obj) {
        if (obj == null) {
            return null;
        }
        //此處obj = BinderProxy, descriptor = "android.app.IActivityManager";
        IActivityManager in = (IActivityManager)obj.queryLocalInterface(descriptor);
        if (in != null) { //此處爲null
            return in;
        }
        return new ActivityManagerProxy(obj);
    }
    ...
}

此時obj爲BinderProxy對象, 記錄着遠程進程system_server中AMS服務的binder線程的handle.

[-> ActivityManagerNative.java::ActivityManagerProxy]

class ActivityManagerProxy implements IActivityManager
{
    public ActivityManagerProxy(IBinder remote){
        mRemote = remote;
    }
}

可知mRemote即是指向AMS服務的BinderProxy對象。

mRemote的transact方法

[-> Binder.java::BinderProxy]

final class BinderProxy implements IBinder {
    public native boolean transact(int code, Parcel data, Parcel reply,
            int flags) throws RemoteException;

mRemote實際上調用了 BinderProxytransact 方法,而transact 調用了native方法mRemote.transact() 方法中,通過jni調用android_os_BinderProxy_transact方法。

android_os_BinderProxy_transact

[-> android_util_Binder.cpp]

static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj,
    jint code, jobject dataObj, jobject replyObj, jint flags)
{
    ...
    //將java Parcel轉爲c++ Parcel
    Parcel* data = parcelForJavaObject(env, dataObj);
    Parcel* reply = parcelForJavaObject(env, replyObj);

    //gBinderProxyOffsets.mObject中保存的是new BpBinder(handle)對象
    IBinder* target = (IBinder*) env->GetLongField(obj, gBinderProxyOffsets.mObject);
    ...

    //此處即是BpBinder::transact()
    status_t err = target->transact(code, *data, reply, flags);
    ...

    //最後根據transact執行具體狀況,拋出相應的Exception
    signalExceptionForError(env, obj, err, true , data->dataSize());
    return JNI_FALSE;
}

gBinderProxyOffsets.mObject中保存的是BpBinder對象, 這是開機時Zygote調用AndroidRuntime::startReg方法來完成jni方法的註冊.

其中register_android_os_Binder()過程就有一個初始並註冊BinderProxy的操做,完成gBinderProxyOffsets的賦值過程. 接下來就進入該方法.

後續分析==

參考資料

簡單明瞭,完全地理解Binder

完全理解Android Binder通訊架構

一篇文章瞭解相見恨晚的 Android Binder 進程間通信機制

相關文章
相關標籤/搜索