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

参考资料

相关推荐
SRC_BLUE_171 小时前
SQLI LABS | Less-39 GET-Stacked Query Injection-Intiger Based
android·网络安全·adb·less
无尽的大道4 小时前
Android打包流程图
android
镭封6 小时前
android studio 配置过程
android·ide·android studio
夜雨星辰4876 小时前
Android Studio 学习——整体框架和概念
android·学习·android studio
邹阿涛涛涛涛涛涛6 小时前
月之暗面招 Android 开发,大家快来投简历呀
android·人工智能·aigc
IAM四十二6 小时前
Jetpack Compose State 你用对了吗?
android·android jetpack·composer
奶茶喵喵叫6 小时前
Android开发中的隐藏控件技巧
android
Winston Wood8 小时前
Android中Activity启动的模式
android
众乐认证8 小时前
Android Auto 不再用于旧手机
android·google·智能手机·android auto
三杯温开水9 小时前
新的服务器Centos7.6 安卓基础的环境配置(新服务器可直接粘贴使用配置)
android·运维·服务器