移步系列Android跨进程通信IPC系列
Parcel类详解
每一个java端的Binder对象(服务端)在初始化时都会对应一个native对象,类型是BBinder,它继承于IBinder类

1 Binder对象的写入

时序图


5713484-9635f897d3851952.jpeg

通过 Parcel的writeStrongBinder方法将Binder对象序列化:

1.1 Parcel.writeStrongBinder(IBinder)

/Parcel.java    /**     * Write an object into the parcel at the current dataPosition(),     * growing dataCapacity() if needed.     */    public final void writeStrongBinder(IBinder val) {        nativeWriteStrongBinder(mNativePtr, val);    }    private static native void nativeWriteStrongBinder(long nativePtr, IBinder val);

调用了native的方法nativeWriteStrongBinder(long,IBinder),这个方法对应JNI的android_os_Parcel_writeStrongBinder()函数

1.2 android_os_Parcel_writeStrongBinder(JNIEnv* env, jclass clazz, jlong nativePtr, jobject object)

代码在android_os_Parcel.cpp 298行

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()函数,返回的是BBinder对象(实际上是JavaBBinder,它继承自BBinder)
  • 然后调用了Parcel-Native的writeStrongBinder函数

1.3 Parcel::writeStrongBinder

代码在Parcel.cpp 872行

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

主要是调用了flatten_binder()函数
这里说一下ProcessState::self() 是获取ProcessState的单例方法

1.4 Parcel::flatten_binder(const sp& /proc/, const sp& binder, Parcel* out)

代码在Parcel.cpp 205行

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) {        //JavaBBinder返回的是this,也就是自己         IBinder *local = binder->localBinder();        //不是本地进程,即跨进程        if (!local) {            //分支一,如果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 {            //分支二,local不为空            //写入JavaBBinder将对应这一段            obj.type = BINDER_TYPE_BINDER;             // 弱引用对象            obj.binder = reinterpret_cast(local->getWeakRefs());            //  this 对象,BBinder本身            obj.cookie = reinterpret_cast(local);        }    } else {        obj.type = BINDER_TYPE_BINDER;        obj.binder = 0;        obj.cookie = 0;    }    return finish_flatten_binder(binder, obj, out);}

这里主要是分别是本地Binder还是远程Binder,对两种Binder采取了两种不同的方式。

  • Binder如果是JavaBBinder,则它的localBinder会返回localBinder。
  • Binder如果是BpBinder,则它的localBinder会返回null
  • 通过上文我们知道ibinderForJavaObject就返回JavaBBinder。所以我们知道走入分支二

  • flat_binder_object是Binder写入对象的结构体,它对应着Binder。

  • flat_binder_object的handle表示Binder对象在Binder驱动中的标志,比如ServiceManager的handle为0。

  • flat_binder_object的type表示当前传输的Binder是本地的(同进程),还是一个Proxy(跨进程)

通过上面代码我们知道这里取得的flat_binder_object对应的值如下

  • type为BINDER_TYPE_BINDER
  • binder为reinterpret_cast(local->getWeakRefs());
  • cookie为reinterpret_cast(local)

binder,cookie保存着Binder对象的指针。最后调用了finish_flatten_binder()函数

1.5 Parcel::finish_flatten_binder()函数

代码在Parcel.cpp 199行

inline static status_t finish_flatten_binder(    const sp& /*binder*/, const flat_binder_object& flat, Parcel* out){    return out->writeObject(flat, false);}

finish_flatten_binder()函数主要是调用writeObject()函数将flat_binder_object写入到out里面里面,最终写入到Binder驱动中

1.6 Parcel::writeObject()函数

代码在Parcel.cpp 1035行

status_t Parcel::writeObject(const flat_binder_object& val, bool nullMetaData){    const bool enoughData = (mDataPos+sizeof(val)) <= mDataCapacity;    const bool enoughObjects = mObjectsSize < mObjectsCapacity;    //分支一    if (enoughData && enoughObjects) {restart_write:         // mObjects 数据写入        *reinterpret_cast(mData+mDataPos) = val;        // remember if it's a file descriptor        if (val.type == BINDER_TYPE_FD) {            if (!mAllowFds) {                // fail before modifying our object index                return FDS_NOT_ALLOWED;            }            mHasFds = mFdsKnown = true;        }        // Need to write meta-data?        if (nullMetaData || val.binder != 0) {            mObjects[mObjectsSize] = mDataPos;            acquire_object(ProcessState::self(), val, this, &mOpenAshmemSize);            mObjectsSize++;        }        return finishWrite(sizeof(flat_binder_object));    }    //分支二    if (!enoughData) {        const status_t err = growData(sizeof(val));        if (err != NO_ERROR) return err;    }    //分支三    if (!enoughObjects) {        size_t newSize = ((mObjectsSize+2)*3)/2;        if (newSize < mObjectsSize) return NO_MEMORY;   // overflow        binder_size_t* objects = (binder_size_t*)realloc(mObjects, newSize*sizeof(binder_size_t));        if (objects == NULL) return NO_MEMORY;        mObjects = objects;        mObjectsCapacity = newSize;    }    goto restart_write;}

三个分支

  • 如果mData和mObjects空间足够,则走分支一
  • 如果mData空间不足,则扩展空间,growData()函数看前面
  • 如果mObjects空间不足,则扩展空间,和mData空间扩展基本相似

调用 * reinterpret_cast(mData+mDataPos) = val; 写入mObject

最后调用finishWrite()函数,就是调整调整 mDataPos 和 mDataSize,上面已经说过了,我就跟踪了。至此整体写入流程已经完成了。

2 Binder对象的读出

Parcel对象的读出,首先还是在Parcel.java这个类里面,对应的方法是

时序图如下:


5713484-ad4370e8a1fdf7a8.png

2.1 Parcel.readStrongBinder()方法

/**     * Read an object from the parcel at the current dataPosition().     */    public final IBinder readStrongBinder() {        return nativeReadStrongBinder(mNativePtr);    }

readStrongBinder()方法内部调用了native的nativeReadStrongBinder()方法,

2.2 Parcel.nativeReadStrongBinder()方法

private static native IBinder nativeReadStrongBinder(long nativePtr);

native方法又对应这个JNI的一个方法,通过上文我们知道,对应的是 /frameworks/base/core/jni/android_os_Parcel.cpp的android_os_Parcel_readStrongBinder()函数

2.3 android_os_Parcel_readStrongBinder()函数

代码在android_os_Parcel.cpp 429行

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;}

函数里面先调用了Parcel-Native的readStrongBinder()函数,然后又用这个函数的返回值作为参数调用了javaObjectForIBinder()函数

2.4 readStrongBinder()函数

代码在Parcel.cpp 1134行

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

主要就是调用了unflatten_binder()函数

readStrongBinder 其实挺简单的,是本地的可以直接用,远程的那个 getStrongProxyForHandle 也是放到后面 ServiceManager 再细说。到这里目标进程就收到原始进程传递过来的 binder 对象了,然后可以转化为 binder 的 interface 调用对应的 IPC 接口。

2.5 unflatten_binder()函数

代码在android_os_Parcel.cpp 293行

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:                //如果是Bn的话,本地直接强转                *out = reinterpret_cast(flat->cookie);                return finish_unflatten_binder(NULL, *flat, in);            case BINDER_TYPE_HANDLE:                //如果是Bp的话,要通过handle构造一个远程的代理对象                *out = proc->getStrongProxyForHandle(flat->handle);                return finish_unflatten_binder(                    static_cast(out->get()), *flat, in);        }    }    return BAD_TYPE;}

这里说下这里的两个分支

  • BINDER_TYPE_BINDER分支:是Bn意味着是同一个进程
  • BINDER_TYPE_HANDLE分支:是Bp意味着是跨进程

再来分下一下这个函数,主要就是两个两个流程

    1. 从Binder驱动中读取一个flat_binder_object对象flat
    1. 根据flat对象的type值来分别处理。如果是BINDER_TYPE_BINDER,则使用cookie中的值,强制转换成指针。如果是BINDER_TYPE_HANDLE,则使用Proxy,即通过getStringProxyForHandle()函数,并根据handle创建BpBinder。

ProcessState::getStrongProxyForHandle()函数后续讲解Binder的时候再详细讲解,这里就不说了。那我们来看一下finish_unflatten_binder()

2.6 finish_unflatten_binder()函数

代码在android_os_Parcel.cpp 286行

inline static status_t finish_unflatten_binder(    BpBinder* /*proxy*/, const flat_binder_object& /*flat*/,    const Parcel& /*in*/){    return NO_ERROR;}

返回NO_ERROR。这时候我们回到了javaObjectForIBinder()函数。

2.7 javaObjectForIBinder()函数

javaObjectgForBinder与ibinderForJavaObject相对应的,把IBinder对象转换成对应的Java层的Object。这个函数是关键。

代码在android_os_Parcel.cpp 547行

jobject javaObjectForIBinder(JNIEnv* env, const sp& val){    if (val == NULL) return NULL;          // One of our own!    if (val->checkSubclass(&gBinderOffsets)) {          //如果是本地的,那么会直接进入这个部分代码,因为这个val         //是写入的时候的同一个对象,gBinderOffsets也是一致的。如         //果val是一种Poxy对象,则不然,会继续往下执行,找到一个         //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?     // BpBinder没有带proxy过来    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);    }      // 创建一个proxy      object = env->NewObject(gBinderProxyOffsets.mClass, gBinderProxyOffsets.mConstructor);    if (object != NULL) {        // 给object的相关字段赋值        LOGDEATH("objectForBinder %p: created new proxy %p !\n", val.get(), object);        // The proxy holds a reference to the native object.         // 把BpBinder(0) 赋值给BinderProxy的mObject        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;}
  • 首先判断是不是同一个进程,如果是同一个进程,则val是JavaBBinder。那么在checkSubclass()函数中,它所包含的gBinderOffsets指针参数传入的gBinderOffsets的指针必然是同一个值,则满足if条件,直接将指针强制转化为JavaBBinder,返回对应的jobject。如果是不是同一个进程,那么val也就是BpBinder
  • 其次,在BpBinder对象中查找是否保存相关的BinderProxy的对象,如果有,向Java层返回这个对象。如果没有,则创建一个BinderProxy对象,并将新创建的BinderProxy对象,attach到BpBinder对象中。

在构造Java对象的时候,上面用到了 binderproxy_offsets_t 结构体 ,那我们就来看下这个结构体

2.8 binderproxy_offsets_t 结构体

代码在android_os_Parcel.cpp 95行

static struct binderproxy_offsets_t{    // Class state.    jclass mClass;    jmethodID mConstructor;    jmethodID mSendDeathNotice;    // Object state.    jfieldID mObject;    jfieldID mSelf;    jfieldID mOrgue;} gBinderProxyOffsets;

gBinderProxyOffsets的初始化是在虚拟机启动的时候(即在 AndroidRuntime::start),最终赋值是在android_os_Parcel.cpp 的中1254行的函数
int_register_android_os_BinderProxy()中,代码如下

static int int_register_android_os_BinderProxy(JNIEnv* env){    jclass clazz = FindClassOrDie(env, "java/lang/Error");1257    gErrorOffsets.mClass = MakeGlobalRefOrDie(env, clazz);    clazz = FindClassOrDie(env, kBinderProxyPathName);    gBinderProxyOffsets.mClass = MakeGlobalRefOrDie(env, clazz);    gBinderProxyOffsets.mConstructor = GetMethodIDOrDie(env, clazz, "", "()V");    gBinderProxyOffsets.mSendDeathNotice = GetStaticMethodIDOrDie(env, clazz, "sendDeathNotice",            "(Landroid/os/IBinder$DeathRecipient;)V");    gBinderProxyOffsets.mObject = GetFieldIDOrDie(env, clazz, "mObject", "J");    gBinderProxyOffsets.mSelf = GetFieldIDOrDie(env, clazz, "mSelf",                                                "Ljava/lang/ref/WeakReference;");    gBinderProxyOffsets.mOrgue = GetFieldIDOrDie(env, clazz, "mOrgue", "J");    clazz = FindClassOrDie(env, "java/lang/Class");    gClassOffsets.mGetName = GetMethodIDOrDie(env, clazz, "getName", "()Ljava/lang/String;");    return RegisterMethodsOrDie(        env, kBinderProxyPathName,        gBinderProxyMethods, NELEM(gBinderProxyMethods));}

通过上面代码我们知道这个类型是android.os.BinderProxy,也就是说代理端Java层的对象是android.os.BinderProxy

2.9

  • 每个进程都会保存多个当前调用过的BpBinder对象
  • 每个BpBinder对象都会保存与之对应的Java层的BinderProxy

将创建的BinderProxy attach到BpBinder的意义在于,通过这种方式,Java应用层偏饭获取同一个Service的IBinder时,获取的是同一个BinderProxy

5713484-13bb6c483859927f.png

3 Parcel读取写入总结

  • 上面介绍了Parcel整个写入读取的流程,最后代替Binder传输的是
    flat_binder_object
  • 在Parcel-Native中,根据跨进程和非跨进程,flat_binder_object的值是不一样的:跨进程的时候flat_binder_object的type是BINDER_TYPE_HANDLE;非跨进程的时候flat_binder_object的type是BINDER_TYPE_BINDER。
  • 客户端的Parcel读取Binder的时候,根据flat_binder_object的type值进行区分对待,返回不同的内容。而写入的时候也是一样的,根据是否是Proxy,来决定写入HANDLE还是BINDER。
  • 最终这些内容都会通过ioctl与Binder驱动进行数据通信。所以最终处理不同进程间的Binder数据传输处理也只能是Binder驱动了。
  • Binder对象传入Binder驱动最底层是转化为flat_binder_object传递的。
  • Parcel是根据从驱动中读取的数据做出不同的处理,如果从Binder驱动中取出的flat_binder_object的type为BINDER_TYPE_HANDLE,则创建BpBinder,在Java层创建的BinderProxy返回;
  • 如果读出的flat_binder_object的type为BINDER_TYPE_BINDER则直接使用cookie的指针,将它装置转为化JavaBBinder,在Java层为原来的Service的Binder对象(相同进程)。
  • 在 /frameworks/base/core/jni/android_util_Binder.cpp 中有两个函数分别是ibinderForJavaObject()函数与javaObjectForIBinder()函数是相互对应的,一个是把Native中对应的IBinder对象化为Java对象,一个是将Java的对象转为化Native中对应的IBinder对象

参考

Android跨进程通信IPC之4——AndroidIPC基础2

更多相关文章

  1. Android(安卓)JNI 高级篇
  2. quick cocos2d-x 与android实现更换头像
  3. Android(安卓)EventBus使用
  4. Drawable和Bitmap转换
  5. Android开发重修
  6. android viewflipper和tabhost效率对比
  7. Android中关于Volley的使用(三)认识Volley的架构
  8. Android(安卓)SharedPreferences的使用.
  9. Timber 源码解析

随机推荐

  1. Android(安卓)问题记录
  2. 获取Android(安卓)手机屏幕宽度和高度以
  3. android多选联系人实现
  4. Android(安卓)Studio Start Failed解决方
  5. Android源码下载
  6. 从中间扩展和缩小
  7. 有关Material Design新特性的详解。
  8. android maven Unable to execute dex: M
  9. Power Profiles for Android
  10. 【android】设置View字体点击变色和边框