Binder 中的 Parcel 数据结构分析(Java)

Android Framework 系列教程:yuandaimaahao.github.io/AndroidFram...

视频教程、源码、答疑服务与进入 Framework 技术交流群请联系微信 zzh0838

Parcel 是一个横跨 Java 层与 Native 层的数据结构,扮演了 Binder 远程过程调用中数据传输载体的角色。客户端将数据写入 Parcel,服务端从 Parcel 中读出数据。接下来我们就从 Java 源码的角度来看看 Parcel 内部的实现原理。

Parcel 支持读写的数据类型如下:

  • Primitives:基本数据类型
  • Primitives Arrays:基本数据类型数组
  • Parcelables:实现了 Parcelable 接口的数据类型
  • Bundle 数据类型:Java 层封装的数据包,仅 Java 层支持
  • Active Objects:IBinder 对象和 FileDescriptor 对象
  • Untyped Containers:未指定泛型类型的 List Map SparseArray

Java 层的 Parcel 实际上只是一层对外提供接口的马甲,其内部核心实现都是通过 JNI 调用 Native 层的 Parcel。

Java 层 Parcel 的初始化

在 Java 层,使用 obtain 方法来获取一个 Parcel 对象:

java 复制代码
Parcel mParcel = Parcel.obtain()

obtain 的具体实现如下:

java 复制代码
// frameworks/base/core/java/android/os/Parcel.java
//缓存池
private static final int POOL_SIZE = 6;
private static final Parcel[] sOwnedPool = new Parcel[POOL_SIZE];

@NonNull
public static Parcel obtain() {
    final Parcel[] pool = sOwnedPool;
    synchronized (pool) {
        Parcel p;
        //从缓存池里找一个 Parcel 对象返回
        for (int i=0; i<POOL_SIZE; i++) {
            p = pool[i];
            if (p != null) {
                //这里只是将对象的引用置为空,不是将对象本身置空
                //引用置空,表示这个对象已被使用,下次 obtain 的时候就直接跳过
                pool[i] = null;
                if (DEBUG_RECYCLE) {
                    p.mStack = new RuntimeException();
                }
                p.mReadWriteHelper = ReadWriteHelper.DEFAULT;
                return p;
            }
        }
    }
    //缓存池里的对象都被使用了,就重新 new 一个
    return new Parcel(0);
}

接下来我们来看看 Parcel 的构造函数:

java 复制代码
// frameworks/base/core/java/android/os/Parcel.java
private Parcel(long nativePtr) {
    if (DEBUG_RECYCLE) {
        mStack = new RuntimeException();
    }
    //Log.i(TAG, "Initializing obj=0x" + Integer.toHexString(obj), mStack);
    init(nativePtr);
}

//接着调用 init
private void init(long nativePtr) {
    if (nativePtr != 0) {
        mNativePtr = nativePtr;
        mOwnsNativeParcelObject = false;
    } else { // nativePtr = 0 ,走这个分支
        mNativePtr = nativeCreate();
        mOwnsNativeParcelObject = true;
    }
}

//接着调用 nativeCreate,nativeCreate 是一个 native 方法
private static native long nativeCreate();

// JNI 函数是在 Zygote 启动时注册的
// frameworks/base/core/jni/AndroidRuntime.cpp
REG_JNI(register_android_os_Parcel)

int register_android_os_Parcel(JNIEnv* env)
{
    jclass clazz = FindClassOrDie(env, kParcelPathName);

    gParcelOffsets.clazz = MakeGlobalRefOrDie(env, clazz);
    gParcelOffsets.mNativePtr = GetFieldIDOrDie(env, clazz, "mNativePtr", "J");
    gParcelOffsets.obtain = GetStaticMethodIDOrDie(env, clazz, "obtain", "()Landroid/os/Parcel;");
    gParcelOffsets.recycle = GetMethodIDOrDie(env, clazz, "recycle", "()V");

    return RegisterMethodsOrDie(env, kParcelPathName, gParcelMethods, NELEM(gParcelMethods));
}

//nativeCreate 对应的 native 函数定义在 frameworks/base/core/jni/android_os_Parcel.cpp 中:

//操作很简单,就是 new 一个 Parcel,然后把地址转成 long 返回给 Java 层
static jlong android_os_Parcel_create(JNIEnv* env, jclass clazz)
{
    Parcel* parcel = new Parcel();
    return reinterpret_cast<jlong>(parcel);
}

//回到 Java 层 init 中:
private void init(long nativePtr) {
    if (nativePtr != 0) {
        mNativePtr = nativePtr;
        mOwnsNativeParcelObject = false;
    } else { // nativePtr = 0 ,走这个分支
        // Native 层返回的地址保存在 mNativePtr 成员变量中
        mNativePtr = nativeCreate();
        mOwnsNativeParcelObject = true;
    }
}

自此,整个 Parcel 初始化过程就完成了,总结一下:

  • 我们通过调用 Parcel 类的静态方法 obtain 来获取一个 Parcel 对象
  • Parcel 中有一个缓存数组 private static final Parcel[] sOwnedPool = new Parcel[POOL_SIZE];,调用 obtain 方法时,会首先从这个缓存中找一个未被使用的 Parcel 对象返回,程序中通过数组中的引用是否为空来标记 Parcel 对象是否被使用
  • 如果缓存数组中的对象都被使用了,就重新 new 一个 Parcel 返回
  • Java 层的 Parcel 是一层提供 Java 层接口的马甲,其核心功能都是通过 Native 层的 Parcel 对象实现的。在构造方法中会通过 JNI 函数 new 一个 Native 层的 Parcel 对象,并把这个对象的内存地址存放在 Java 层的 mNativePtr 成员中

Java 层 Parcel 的回收与销毁

Java 层的 Parcel 在使用完成后,需要调用 recycle 方法来执行回收操作:

java 复制代码
    // frameworks/base/core/java/android/os/Parcel.java
    public final void recycle() {
        if (DEBUG_RECYCLE) mStack = null;
        //调用 native 层的回收操作
        freeBuffer();

        final Parcel[] pool;
        if (mOwnsNativeParcelObject) { //在 init 中初始化为 true,走这个分支
            pool = sOwnedPool;
        } else {
            mNativePtr = 0;
            pool = sHolderPool;
        }

        //将缓存池中第一个为 null 的引用指向当前对象
        synchronized (pool) {
            for (int i=0; i<POOL_SIZE; i++) {
                if (pool[i] == null) {
                    pool[i] = this;
                    return;
                }
            }
        }
    }

    private void freeBuffer() {
        if (mOwnsNativeParcelObject) { //在 init 中初始化为 true,走这个分支
            updateNativeSize(nativeFreeBuffer(mNativePtr));
        }
        mReadWriteHelper = ReadWriteHelper.DEFAULT;
    }

    // nativeFreeBuffer 是一个 native 方法,对应的 native 函数是 android_os_Parcel_freeBuffer
    private static native long nativeFreeBuffer(long nativePtr);
    static jlong android_os_Parcel_freeBuffer(JNIEnv* env, jclass clazz, jlong nativePtr)
    {
        Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
        if (parcel != NULL) {
            parcel->freeData(); //回收内存,修改一些标记变量
            return parcel->getOpenAshmemSize();
        }
        return 0;
    }

    void Parcel::freeData()
    {
        freeDataNoInit();
        initState();
    }

    void Parcel::freeDataNoInit()
    {
        if (mOwner) {
            LOG_ALLOC("Parcel %p: freeing other owner data", this);
            //ALOGI("Freeing data ref of %p (pid=%d)", this, getpid());
            mOwner(this, mData, mDataSize, mObjects, mObjectsSize, mOwnerCookie);
        } else { // mOwner 初始化为空,走这个分支
            LOG_ALLOC("Parcel %p: freeing allocated data", this);
            //清理 mObjects 数组成员指向的 flat_binder_object 对象
            releaseObjects();
            //修改一些标记变量的值
            if (mData) {
                LOG_ALLOC("Parcel %p: freeing with %zu capacity", this, mDataCapacity);
                pthread_mutex_lock(&gParcelGlobalAllocSizeLock);
                if (mDataCapacity <= gParcelGlobalAllocSize) {
                gParcelGlobalAllocSize = gParcelGlobalAllocSize - mDataCapacity;
            } else {
                gParcelGlobalAllocSize = 0;
            }
            if (gParcelGlobalAllocCount > 0) {
              gParcelGlobalAllocCount--;
            }
            pthread_mutex_unlock(&gParcelGlobalAllocSizeLock);
            //清理 mData 指向的内存
            free(mData);
        }
         //清理 mObjects 指向的内存
        if (mObjects) free(mObjects);
    }
}

Parcel 中定义了 finalize 方法,用于在 gc 时回收对象:

java 复制代码
    // frameworks/base/core/java/android/os/Parcel.java
    @Override
    protected void finalize() throws Throwable {
        if (DEBUG_RECYCLE) {
            if (mStack != null) {
                Log.w(TAG, "Client did not call Parcel.recycle()", mStack);
            }
        }
        // 接着调用 destroy()
        destroy();
    }

    private void destroy() {
        if (mNativePtr != 0) {
            if (mOwnsNativeParcelObject) {
                //接着调用 nativeDestroy
                nativeDestroy(mNativePtr);
                updateNativeSize(0);
            }
            mNativePtr = 0;
        }
        mReadWriteHelper = null;
    }

    // nativeDestroy 是一个本地函数
    private static native void nativeDestroy(long nativePtr);

    // nativeDestroy 对应 native 层 
    // frameworks/base/core/jni/android_os_Parcel.cpp 中的 android_os_Parcel_destroy 函数
    static void android_os_Parcel_destroy(JNIEnv* env, jclass clazz, jlong nativePtr)
    {
        Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
        //直接清除内存
        delete parcel;
    }

Java 层 Parcel 整型数据读写

Java 层提供了 writeInt 接口写入 Int 型数据:

java 复制代码
    // frameworks/base/core/java/android/os/Parcel.java
    public final void writeInt(int val) {
        nativeWriteInt(mNativePtr, val);
    }
    //进一步调用到 nativeWriteInt
    private static native void nativeWriteInt(long nativePtr, int val);
    //nativeWriteInt 是一个 native 方法
    //nativeWriteInt 对应的 native 函数是
    //frameworks/base/core/jni/android_os_Parcel.cpp 中的 android_os_Parcel_writeInt 函数
    static void android_os_Parcel_writeInt(JNIEnv* env, jclass clazz, jlong nativePtr, jint val) {
    //实际上就是调用 Native 层的 writeInt32
    Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
    if (parcel != NULL) {
        const status_t err = parcel->writeInt32(val);
        if (err != NO_ERROR) {
            signalExceptionForError(env, clazz, err);
        }
    }
}

可以看出 java 层只是提供一个 writeInt 的对外接口,实际操作还是通过 native 层的 writeInt32 来完成,这部分在上文已做具体分析,具体可以参考Binder 中的 Parcel 数据结构分析(C++)

接下来我们来看 Java 层的 Int 数据读取操作:

java 复制代码
    // frameworks/base/core/java/android/os/Parcel.java
    public final int readInt() {
        return nativeReadInt(mNativePtr);
    }

    //实际是调用 native 方法
    private static native int nativeReadInt(long nativePtr); 

    //对应的 native 函数
    //frameworks/base/core/jni/android_os_Parcel.cpp 中的 android_os_Parcel_readInt 函数
    static jint android_os_Parcel_readInt(jlong nativePtr)
    {
        Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
        if (parcel != NULL) {
            //仍然是调用 native 层的 readInt32
            return parcel->readInt32();
        }
        return 0;
    }   

可以看出 java 层只是提供一个 readInt 的对外接口,实际操作还是通过 native 层的 readInt32 来完成,这部分在上文已做具体分析,具体可以参考Binder 中的 Parcel 数据结构分析(C++)

String 类型的读取和 Int 类型一样也是委托给 Native 来做具体实现,这部分内容就留给读者自己分析了。

Java 层 Parcel IBinder 数据读写

接下来我们来看看 IBinder 类型的数据是怎么读写的:

java 复制代码
    // frameworks/base/core/java/android/os/Parcel.java
    public final void writeStrongBinder(IBinder val) {
        nativeWriteStrongBinder(mNativePtr, val);
    }
    private static native void nativeWriteStrongBinder(long nativePtr, IBinder val);

    //对应的 native 函数
    //frameworks/base/core/jni/android_os_Parcel.cpp 中的 android_os_Parcel_writeStrongBinder 函数
static void android_os_Parcel_writeStrongBinder(JNIEnv* env, jclass clazz, jlong nativePtr, jobject object)
{
    Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
    if (parcel != NULL) {
        const status_t err = parcel->writeStrongBinder(ibinderForJavaObject(env, object));
        if (err != NO_ERROR) {
            signalExceptionForError(env, clazz, err);
        }
    }
}    

这里仍然是调用 native 层的 writeStrongBinder 来完成具体的读写,这部分在上文已做具体分析,具体可以参考Binder 中的 Parcel 数据结构分析(C++)

接下来我们把重点放在 ibinderForJavaObject,ibinderForJavaObject 的作用是把 java 层的 IBinder 转换为 Native 层的 IBinder:

在查看源码之前我们先回顾下 Binder Java 层的总体架构:

在 Java 层,服务端类是 Binder,其内部有一个 long 型数据是一个 Native 层指针,指向一个 native 层的 BBinder 对象。

在 Java 层,客户端类或者叫代理类是 BinderProxy,内部有一个 long 型数据是一个 Native 层指针,指向一个 native 层的 JavaBBinder 对象。JavaBBinder 对象内部又有指针执行一个 BBinder 对象,

接下来,我们就来看看 ibinderForJavaObject 的具体实现:

cpp 复制代码
sp<IBinder> ibinderForJavaObject(JNIEnv* env, jobject obj)
{
    if (obj == NULL) return NULL;

    //obj 类型是 Binder,服务端类
    if (env->IsInstanceOf(obj, gBinderOffsets.mClass)) {
        //获取到 Java 层 Binder 对应的 Native 层 JavaBBinderHolder 对象
        JavaBBinderHolder* jbh = (JavaBBinderHolder*)
            env->GetLongField(obj, gBinderOffsets.mObject);
        //返回 JavaBBinderHolder 对象内部指针指向的 BBinder 对象
        return jbh->get(env, obj);
    }

    //obj 类型是 BinderProxy,客户端类
    if (env->IsInstanceOf(obj, gBinderProxyOffsets.mClass)) {
        //返回 Java 层 BinderProxy 对应的 Native 层 BpBinder 对象
        return getBPNativeData(env, obj)->mObject;
    }

    ALOGW("ibinderForJavaObject: %p is not a Binder object", obj);
    return NULL;
}

接下来我们再来看看 IBinder 的读操作:

java 复制代码
    // frameworks/base/core/java/android/os/Parcel.java
    public final IBinder readStrongBinder() {
        return nativeReadStrongBinder(mNativePtr);
    }
    private static native IBinder nativeReadStrongBinder(long nativePtr);

//对应的 native 函数
//frameworks/base/core/jni/android_os_Parcel.cpp 中的 android_os_Parcel_readStrongBinder 函数
static jobject android_os_Parcel_readStrongBinder(JNIEnv* env, jclass clazz, jlong nativePtr)
{
    Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
    if (parcel != NULL) {
        return javaObjectForIBinder(env, parcel->readStrongBinder());
    }
    return NULL;
}

这里仍然是调用 native 层的 readStrongBinder 来完成具体的读写,这部分在上文已做具体分析,具体可以参考Binder 中的 Parcel 数据结构分析(C++)

接下来我们重点关注一下 javaObjectForIBinder 函数,这个函数用于将 Native 层的 IBinder 对象转换为 Java 层的 IBinder 对象:

cpp 复制代码
//frameworks/base/core/jni/android_util_Binder.cpp
jobject javaObjectForIBinder(JNIEnv* env, const sp<IBinder>& val)
{
    if (val == NULL) return NULL;

    if (val->checkSubclass(&gBinderOffsets)) {
        // It's a JavaBBinder created by ibinderForJavaObject. Already has Java object.
        jobject object = static_cast<JavaBBinder*>(val.get())->object();
        LOGDEATH("objectForBinder %p: it's our own %p!\n", val.get(), object);
        return object;
    }

    //构造 BinderProxyNativeData 结构体
    BinderProxyNativeData* nativeData = new BinderProxyNativeData();
    nativeData->mOrgue = new DeathRecipientList;
    nativeData->mObject = val;

    //gBinderProxyOffsets 中保存了 BinderProxy 类相关的信息
    //调用 Java 层 GetInstance 方法获得一个 BinderProxy 对象
    jobject object = env->CallStaticObjectMethod(gBinderProxyOffsets.mClass,
            gBinderProxyOffsets.mGetInstance, (jlong) nativeData, (jlong) val.get());
    if (env->ExceptionCheck()) { //异常处理
        // In the exception case, getInstance still took ownership of nativeData.
        return NULL;
    }
    //数据做一些检查修改操作
    BinderProxyNativeData* actualNativeData = getBPNativeData(env, object);
    if (actualNativeData == nativeData) {
        // Created a new Proxy
        uint32_t numProxies = gNumProxies.fetch_add(1, std::memory_order_relaxed);
        uint32_t numLastWarned = gProxiesWarned.load(std::memory_order_relaxed);
        if (numProxies >= numLastWarned + PROXY_WARN_INTERVAL) {
            // Multiple threads can get here, make sure only one of them gets to
            // update the warn counter.
            if (gProxiesWarned.compare_exchange_strong(numLastWarned,
                        numLastWarned + PROXY_WARN_INTERVAL, std::memory_order_relaxed)) {
                ALOGW("Unexpectedly many live BinderProxies: %d\n", numProxies);
            }
        }
    } else {
        delete nativeData;
    }

    //返回 BinderProxy
    return object;
}

参考资料

相关推荐
GH小杨6 分钟前
Kotlin D1
android·开发语言·kotlin
&岁月不待人&9 分钟前
Kotlin 协程(三)协程的常用关键字使用及其比较
android·开发语言·kotlin
Java资深爱好者28 分钟前
如何在Android中实现图片加载和缓存
android·缓存
解压专家6661 小时前
安卓ZArchiver与解压专家对比评测
android
zhangphil5 小时前
Android Coil3缩略图、默认占位图placeholder、error加载错误显示,Kotlin(5)
android·kotlin
python资深爱好者12 小时前
Android中的触摸事件是如何传递和处理的
android
飞猿_SIR13 小时前
Exoplayer2源码编译FFmpeg拓展模块实现音频软解码
android·ffmpeg·音视频
Ya-Jun13 小时前
Android Studio安装与配置详解
android·ide·android studio
m0_7482463514 小时前
MySQL Workbench安装教程以及菜单汉化
android·数据库·mysql
峥嵘life16 小时前
Android 系统开发的指导文档
android