Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android Binder跨進程與非跨進程的傳輸異同源碼分析

Android Binder跨進程與非跨進程的傳輸異同源碼分析

編輯:關於Android編程

前兩天看到Service的onBind函數返回值的三種情況(擴展Binder對象,Messenger,aidl),一直在想他們內部的實現有什麼不一樣的,網上很多文章都介紹了Service的綁定過程,但是並沒有介紹對於跨進程與非跨進程,對於不同的返回值,其具體有什麼區別,以及具體是怎麼實現的。這篇文章就根據源碼分析Android究竟是在哪部分來控制跨進程與非跨進程Binder的傳輸的,Binder究竟是怎麼傳輸的。

首先看一下Service的綁定中,Binder跨進程與非跨進程的區別代碼中的體現。

Service跨進程綁定與非跨進程綁定的表象

想知道具體是怎麼樣的,其實直接在ServiceConnection的onServiceConnection回調函數中,以及在Service的onBind中分別打印一下Binder就好了,如下代碼

//Service onBind
public Binder onBind(){
    Binder binder ; //這裡並沒有初始化,可以使擴展Binder,也可以是aidl的Stub
    Log.i("TestLog","onBind: "+binder.hashCode()+","+binder.toString());
    return binder;
}

//ServiceConnection
ServiceConnection connection = new ServiceConnection(){
    public void onServiceConnection(ComponentName component, Binder binder){
        Log.i("TestLog","onServiceConnection: "+binder.hashCode()+","+binder.toString());
    }
}

通過設置Service的android:process,可以將Service遠程進程與相同進程進行實驗。
通過log可以發現,如果是跨進程的話,在ServiceConnection的onServiceConnection返回的結果是一個BinderProxy對象,與onBind中打印的結果是不一致的。如果是非跨進程,在onServiceConnection與onBind的打印的內容是一模一樣的。這裡跟onBind是返回擴展Binder還是返回擴展aidl自動產生Stub類是沒有關系的,只跟是否跨進程有關

從這裡可以看出,如果是跨進程則返回BinderProxy,非跨進程則返回原來在onBind中返回的對象。我困惑的問題是,Android是怎麼實現這個根據不同的進程來返回不同的結果呢?下面先看一下Service的綁定過程是否有針對不同進程的區別處理。

Service綁定過程

綁定過程

首先簡單介紹一下Service的綁定過程:
1. Activity通過bindService請求綁定相應的Service(由Intent指定),並設置了ServiceConnection回調接口。
2. 實際上會調用ContextImpl.bindService,然後調用ActivityManagerNative.getDefault().bindService,也就是通過IPC調用ActivityManagerService的bindService函數。
3. 在ActivityManagerService的bindService函數中,先通過retrieve創建ServiceRecord,這個就是對應著我們想要綁定的Service
4. 然後根據結果創建ConnectionRecord,將ServiceConnection保存在這裡面。ConnectionRecord保存在一個ArrayList中,可能有多個客戶端想要綁定。
5. 接著在bringUpServiceLocked函數中判斷是否需要重新創建一個進程來運行Service,根據android:processd的設置
6. 啟動服務,通過ApplicationThreadProxy(app.thread)遠程調用ApplicationThread的scheduleCreateService來發送Message給主線程消息循環(ActivityThread,ActivityThread.H),啟動服務。
7. 綁定服務,通過ApplicationThreadProxy(app.thread)遠程調用ApplicationThread的scheduleBindService來發送Message給主線程消息循環(ActivityThread,ActivityThread.H),綁定服務(handleBindService)。這裡看一下源碼:

try {
    if (!data.rebind) {
        IBinder binder = s.onBind(data.intent);
        ActivityManagerNative.getDefault().publishService(
            data.token, data.intent, binder);
    } else {
        s.onRebind(data.intent);
        ActivityManagerNative.getDefault().serviceDoneExecuting(
            data.token, 0, 0, 0);
    }
    ensureJitEnabled();
} catch (RemoteException ex) {
}
在綁定服務中,調用Service的onBind方法得到Binder,並且通過ActivityManagerNative.getDefault().publishService發布服務。 在publishServcie中使用ServiceRecord獲取ConnectionRecord,最終調用保存在ConnectionRecord裡面的ServiceConnection的onServiceConnection函數。其中ServiceRecord,ServiceConnection都是Binder類,可以通過IPC訪問。

這裡只是簡單介紹一下綁定Service的流程,也並沒有把具體對象名稱寫出來。如果想要更詳細的了解整個過程可以去看一下老羅的博客,其實最好是照著別人的博客看一下源碼。

綁定過程是否處理了不同onBind返回值

我一直覺得是在綁定服務的過程中會針對不同的onBind返回值有不同的處理,但實際上並沒有。看第7步中的源碼就知道了,上面的過程中,並沒有針對不同的onBind返回值進行特殊處理。

既然綁定過程中沒有對不同進程進行處理,那麼只能看看Parcel了。而實際上確實是在Parcel中有體現出來了跨進程與非跨進程的區別。下面看看Parcel讀取和寫入Binder。

Parcel中寫入讀取Binder

Parcel寫入Binder

Parcel寫入Binder是通過writeStrongBinder的,Java層的writeStrongBinder是一個native函數,對應的JNI實現為:

static void android_os_Parcel_writeStrongBinder(JNIEnv* env, jclass clazz, jlong nativePtr, jobject object)
{
    Parcel* parcel = reinterpret_cast(nativePtr);
    if (parcel != NULL) {
        const status_t err = parcel->writeStrongBinder(ibinderForJavaObject(env, object));
        if (err != NO_ERROR) {
            signalExceptionForError(env, clazz, err);
        }
    }
}

其中ibinderForJavaObject是將java層的對象轉換成native層的Binder對象,實際上對應的是JavaBBinder。

然後native層的parcel會調用它的writeStrognBinder:

status_t Parcel::writeStrongBinder(const sp& val)
{
    return flatten_binder(ProcessState::self(), val, this);
}

最終調用flatten_binder:

status_t flatten_binder(const sp& /*proc*/,
    const sp& binder, Parcel* out)
{
    flat_binder_object obj;

    obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
    if (binder != NULL) {
        IBinder *local = binder->localBinder();   //JavaBBinder返回的是this,也就是自己
        if (!local) {
            BpBinder *proxy = binder->remoteBinder();
            if (proxy == NULL) {
                ALOGE("null proxy");
            }
            const int32_t handle = proxy ? proxy->handle() : 0;
            obj.type = BINDER_TYPE_HANDLE;
            obj.binder = 0; /* Don't pass uninitialized stack data to a remote process */
            obj.handle = handle;
            obj.cookie = 0;
        } else {// 寫入JavaBBinder將會對應這一段。
            obj.type = BINDER_TYPE_BINDER;
            obj.binder = reinterpret_cast(local->getWeakRefs());
            obj.cookie = reinterpret_cast(local);   //把對應的JavaBBinder的指針轉換成整形uintptr_t
        }
    } else {
        obj.type = BINDER_TYPE_BINDER;
        obj.binder = 0;
        obj.cookie = 0;
    }

    return finish_flatten_binder(binder, obj, out); //finish_flatten_binder是將obj寫入到out裡面。
}

這裡就根據是本地binder還是遠程binder,對Binder的寫入采取了兩種不同的方式。Binder如果是JavaBBinder,則它的localBinder會返回localBinder,如果是BpBinder則localBinder會為null。我們寫入的時候,ibinderForJavaObject就返回的是JavaBBinder。flat_binder_object是Binder寫入的時候的對象,它對應著Binder。handle表示Binder對象在Binder驅動中的標志,比如ServiceManager的handle為0。type表示當前傳輸的Binder是本地的(同進程),還是一個proxy(跨進程)。binder,cookie保存著Binder對象的指針。finish_flatten_binder會將obj寫入到out裡面,最終寫入到Binder驅動中,寫入部分也就完了。從上面的流程可以看出,在寫入的時候,Parcel會針對不同的Binder(BBinder/JavaBBinder,BpBinder)有不同的處理,而他們確實就是對應著跟Service端Binder是同一個進程的還是不同進程的情況。

Parcel讀取Binder

接下來就是讀取了,同樣的,Java層的Parcel也是通過native函數來讀取的。在這裡我們從最底層開始分析,首先從unflatten_binder開始:

unflatten_binder:

status_t unflatten_binder(const sp& proc,
    const Parcel& in, sp* out)
{
    const flat_binder_object* flat = in.readObject(false);

    if (flat) {
        switch (flat->type) {
            case BINDER_TYPE_BINDER:
                *out = reinterpret_cast(flat->cookie);
                return finish_unflatten_binder(NULL, *flat, in);
            case BINDER_TYPE_HANDLE:
                *out = proc->getStrongProxyForHandle(flat->handle);
                return finish_unflatten_binder(
                    static_cast(out->get()), *flat, in);
        }
    }
    return BAD_TYPE;
}

首先從Binder驅動中讀取一個flat_binder_object—flat。flat的處理是關鍵,它會根據flat->type的值分別處理,如果是BINDER_TYPE_BINDER,則使用cookie中的值強制轉換成指針。如果是BINDER_TYPE_HANDLE,則使用Proxy,getStringProxyForHandle會返回BpBinder。而調用unflatten_binder的是native層的Parcel的readStringBinder。

sp Parcel::readStrongBinder() const
{
    sp val;
    unflatten_binder(ProcessState::self(), *this, &val);
    return val;
}

調用readStrongBinder的是jni函數的實現:

static jobject android_os_Parcel_readStrongBinder(JNIEnv* env, jclass clazz, jlong nativePtr)
{
    Parcel* parcel = reinterpret_cast(nativePtr);
    if (parcel != NULL) {
        return javaObjectForIBinder(env, parcel->readStrongBinder());
    }
    return NULL;
}

javaObjectForIBinder與ibinderForJavaObject相對應,把IBinder對象轉換成對應的Java層的Object。這個函數是關鍵。看看它的實現:

jobject javaObjectForIBinder(JNIEnv* env, const sp& val)
{
     if (val == NULL) return NULL;

     if (val->checkSubclass(&gBinderOffsets)) {  //如果是本地的,那麼會直接進入這部分代碼,因為這個val是寫入的時候同一個對象,gBinderOffsets也是一致。如果val是一種proxy對象,則不然,會繼續往下執行找到一個Proxy對象。
         // One of our own!
         jobject object = static_cast(val.get())->object();
         LOGDEATH("objectForBinder %p: it's our own %p!\n", val.get(), object);
         return object;
     }

     // For the rest of the function we will hold this lock, to serialize
     // looking/creation of Java proxies for native Binder proxies.
     AutoMutex _l(mProxyLock);

     // Someone else's...  do we know about it?
     jobject object = (jobject)val->findObject(&gBinderProxyOffsets);
     if (object != NULL) {
        jobject res = jniGetReferent(env, object);
        if (res != NULL) {
            ALOGV("objectForBinder %p: found existing %p!\n", val.get(), res);
            return res;
        }
        LOGDEATH("Proxy object %p of IBinder %p no longer in working set!!!", object, val.get());
        android_atomic_dec(&gNumProxyRefs);
        val->detachObject(&gBinderProxyOffsets);
        env->DeleteGlobalRef(object);
    }

    object = env->NewObject(gBinderProxyOffsets.mClass, gBinderProxyOffsets.mConstructor);//gBinderProxyOffsets保存的是Java層BinderProxy的信息,這裡也是創建BinderProxy。
    if (object != NULL) {
        LOGDEATH("objectForBinder %p: created new proxy %p !\n", val.get(), object);
        // The proxy holds a reference to the native object.
        env->SetLongField(object, gBinderProxyOffsets.mObject, (jlong)val.get());
        val->incStrong((void*)javaObjectForIBinder);

        // The native object needs to hold a weak reference back to the
        // proxy, so we can retrieve the same proxy if it is still active.
        jobject refObject = env->NewGlobalRef(
                env->GetObjectField(object, gBinderProxyOffsets.mSelf));
        val->attachObject(&gBinderProxyOffsets, refObject,
                jnienv_to_javavm(env), proxy_cleanup);

        // Also remember the death recipients registered on this proxy
        sp drl = new DeathRecipientList;
        drl->incStrong((void*)javaObjectForIBinder);
        env->SetLongField(object, gBinderProxyOffsets.mOrgue, reinterpret_cast(drl.get()));

        // Note that a new object reference has been created.
        android_atomic_inc(&gNumProxyRefs);
        incRefsCreated(env);
    }

    return object;
}
//IBinder
checkSubclass(const void* subclassID) const
{
    return subclassID == &gBinderOffsets;
}

上面的處理中,如果是跟Service是同一個進程,也就是val是JavaBBinder。那麼在checkSubclass中,它所包含的gBinderOffsets指針與參數傳入的gBinderOffsets的指針必然是同一個值,則滿足if條件。直接將指針強制轉換成JavaBBinder,返回對應的jobject。如果是不同的進程,那麼val也就會是BpBinder,最終會返回一個BinderProxy,不同的進程這一部分網上很多介紹Binder的文章都介紹了。

Parcel讀取寫入總結

上面介紹了Parcel整個寫入讀取的流程,最後代替Binder傳輸的是flat_binder_object。在native的Parcel中,根據跨進程還是非跨進程,flat_binder_object的值是不一樣的,跨進程的時候flat_binder_object的type為BINDER_TYPE_HANDLE,非跨進程的時候flat_binder_object的type為BINDER_TYPE_BINDER。在這裡已經可以發現跨進程與非跨進程的時候傳輸的數據的區別了。客戶端的Parcel在讀取Binder的時候,根據是flat_binder_object的type的值進行區分對待,返回不同的內容。

Binder驅動的處理

但是我們還沒有發現Android是如何區分跨進程與非跨進程的呢?顯然它的實現在Binder驅動裡面。關於如何判斷讀取的時候是跟Service同一個進程還是不同的進程,腦子裡隨便一想基本就能夠想到怎麼去實現了。因為每個進程進行系統調用陷入內核的時候,內核的當然是可以知道當前進入內核空間的進程的信息了啦,這樣就可以判斷當前請求讀取信息的是跟Service同一個進程還是不同的進程了。

實際上Binder驅動保存著Service端的Binder地址和handle的信息,將兩者相互映射,根據不同的服務端進程和客戶端進程來區別處理。

總結

實際上真正控制跨進程與非跨進程返回Binder類型的,不是綁定服務的過程,而是Binder驅動。Parcel是根據從驅動中讀取的數據作出不同的處理,而Binder驅動真正控制。如果是與Service端同一個進程則返回Binder(在底層是Binder指針),如果是不同的進程則返回handle。真正在傳輸的過程中,是作為flat_binder_object對象來傳遞的。

其實也可以在Service的綁定過程中來根據是否是相同進程來進行處理的,但那樣就局限於Service的綁定部分了,而Android中用的Binder傳輸的實在是太多了,所以直接在Binder驅動中進行控制更加方便。


積跬步,至千裡;積小流,成江海

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved