Android序列化之Parcel源码分析(2)

1.Parcel.java

Android可以通过Parcel进行数据序列化,IPC时进行数据传输。Parcel类似快递箱,各种数据都支持,从Parcel.java(frameworks\base\core\java\android\os\Parcel.java)可以看到读写方法都是native实现的,支持基本数据类型、数组、集合、map、对象、文件描述符、IBinder等读写。本文基于aosp12进行分析。
在这里插入图片描述

2.Parcelable和Parcel的关系

我们平时基本都是创建Parcelable的实现类,很少直接使用Parcel,那两者有什么关系呢?其实,Parcelable也是使用Parcel来进行数据读写。可以看下面示例:

public class MyData implements Parcelable {
    
    
  
    private String data;

    protected MyData(Parcel in) {
    
    
        data = in.readString();
    }

    // 实现Parcelable必须提供这个字段,表示通过之前写入的那个Parcel创建Parcelable实例
    public static final Creator<MyData> CREATOR = new Creator<MyData>() {
    
    
        @Override
        public MyData createFromParcel(Parcel in) {
    
    
            return new MyData(in);
        }

        @Override
        public MyData[] newArray(int size) {
    
    
            return new MyData[size];
        }
    };

    public String getData() {
    
    
        return data;
    }

    public void setData(String data) {
    
    
        this.data = data;
    }
   // 封装类型描述
    @Override
    public int describeContents() {
    
    
        return 0;
    }

   // 将数据写入到Parcel
    @Override
    public void writeToParcel(Parcel dest, int flags) {
    
    
        dest.writeString(data);
    }
}

3.Parcel写入数据源码分析

Parcel作为数据序列化的容器,就好比快递寄件和取件,双方获取出来的数据格式是一致的。我们可以创建一个简单的aidl,编译查看生成的代码如下:

// IMyData.aidl
package com.example.parcel;

interface IMyData {
    
    
    void writeString(String data);
}

在这里插入图片描述

接下来我们就分析writeString()/readWrite()过程,java层写入调用代码如下:

// 写入数据
android.os.Parcel _data = android.os.Parcel.obtain(); // 获取Parcel对象,会创建一个本地对象,并进行初始化
_data.writeInterfaceToken(DESCRIPTOR); // 写入IBinder接口标志,一般为全类名,用户数据校验
_data.writeString(data);// 写入数据

3.1.java层Parcel创建

先从缓存池中获取,获取不到就创建新的。可以看到最后调用nativeCreate()​来初始化,mNativePtr​用来保存Java层Parcel对应的C++层Parcel对象的地址

// frameworks/base/core/java/android/os/Parcel.java
/**
 * Retrieve a new Parcel object from the pool.
 */
public static Parcel obtain() {
    
    
    Parcel res = null;
    ...
    // When no cache found above, create from scratch; otherwise prepare the
    // cached object to be used
    if (res == null) {
    
    
        res = new Parcel(0);
    } else {
    
     
        res.mReadWriteHelper = ReadWriteHelper.DEFAULT;
    }
    return res;
}

private Parcel(long nativePtr) {
    
     
    init(nativePtr);
}

private void init(long nativePtr) {
    
    
    if (nativePtr != 0) {
    
    
        mNativePtr = nativePtr;
        mOwnsNativeParcelObject = false;
    } else {
    
    
        // 传过来mNativePtr为0,执行这个分支
        mNativePtr = nativeCreate();
        mOwnsNativeParcelObject = true;
    }
}

private static native long nativeCreate();

3.2.native层Parcel创建

接下来我们继续看native层操作,native层Parcel构造方法中初始化相关变量,具体的含义如下:

// frameworks\base\core\jni\android_os_Parcel.cpp
static jlong android_os_Parcel_create(JNIEnv* env, jclass clazz)
{
    
    
    Parcel* parcel = new Parcel();
    return reinterpret_cast<jlong>(parcel);
}

// frameworks\native\libs\binder\Parcel.cpp
Parcel::Parcel()
{
    
    
    initState();
}
void Parcel::initState()
{
    
    
    mError = NO_ERROR;
    mData = nullptr;
    mDataSize = 0;
    mDataCapacity = 0;
    mDataPos = 0;
    ...
}
// 含义
status_t            mError;          // 错误码
const uint8_t*      mData;           // Parcel中存储的数据
size_t              mDataSize;       // Parcel中已经存储的数据大小
size_t              mDataCapacity;   // 最大存储能力
size_t              mDataPos;        // 数据指针

3.3写入IBinder接口标识符

// frameworks\base\core\jni\android_os_Parcel.cpp
static void android_os_Parcel_writeInterfaceToken(JNIEnv* env, jclass clazz, jlong nativePtr,
                                                  jstring name)
{
    
    
    Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
    if (parcel != nullptr) {
    
    
        InterfaceDescriptorString descriptor(env, name);// 获取接口标识名称,可以不用关心
        parcel->writeInterfaceToken(reinterpret_cast<const char16_t*>(descriptor.str()),
                                    descriptor.size());
    }
}

// frameworks\native\libs\binder\Parcel.cpp
status_t Parcel::writeInterfaceToken(const char16_t* str, size_t len) {
    
    
    ... // 判断为maybeKernelFields,先不用管
    return writeString16(str, len);
}
status_t Parcel::writeString16(const char16_t* str, size_t len)
{
    
    
    if (str == nullptr) return writeInt32(-1);
    status_t err = writeInt32(len);
    if (err == NO_ERROR) {
    
    
        len *= sizeof(char16_t);
        uint8_t* data = (uint8_t*)writeInplace(len+sizeof(char16_t));
        if (data) {
    
    
            memcpy(data, str, len);
	    // env->GetStringRegion
            *reinterpret_cast<char16_t*>(data+len) = 0;
            return NO_ERROR;
        }
        err = mError;
    }
    return err;
}

从上面知道writeInterfaceToken()​调用了writeString16()​,下面接着看这个方法。大致有如下几个步骤:

  • writeAligned:“对齐”,保证Parcel的容量可用,如果不够会扩展,同时一定mDataPos指针到新的位置
  • writeInplace:计算复制数据的目标地址,写入到mData
  • memcpy:分配新的数据内存
// frameworks\native\libs\binder\Parcel.cpp
// ------------------------- 步骤1:“对齐”,保证Parcel的容量可用,如果不够会扩展 -------------------------
status_t Parcel::writeInt32(int32_t val)
{
    
    
    return writeAligned(val);
}
template<class T>
status_t Parcel::writeAligned(T val) {
    
    
    static_assert(PAD_SIZE_UNSAFE(sizeof(T)) == sizeof(T));
    static_assert(std::is_trivially_copyable_v<T>);

    if ((mDataPos+sizeof(val)) <= mDataCapacity) {
    
    
restart_write:
        memcpy(mData + mDataPos, &val, sizeof(val)); // 内存拷贝
        return finishWrite(sizeof(val));
    }
    // 动态增长
    status_t err = growData(sizeof(val));
    if (err == NO_ERROR) goto restart_write;
    return err;
}
// 写入完成后,移动DataPos到新的位置
status_t Parcel::finishWrite(size_t len)
{
    
    
    if (len > INT32_MAX) {
    
    
        return BAD_VALUE;
    }
    mDataPos += len;
    if (mDataPos > mDataSize) {
    
    
        mDataSize = mDataPos;
    }
    return NO_ERROR;
}
// ------------------------- 步骤1:计算复制数据的目标地址,写入到mData -------------------------
void* Parcel::writeInplace(size_t len)
{
    
    
    if (len > INT32_MAX) {
    
    
        return nullptr;
    }
    // 计算pad_size
    const size_t padded = pad_size(len);
    if (mDataPos+padded < mDataPos) {
    
    
        return nullptr;
    }
    // 数据填充
    if ((mDataPos+padded) <= mDataCapacity) {
    
    
restart_write:
        uint8_t* const data = mData+mDataPos;
        if (padded != len) {
    
    
#if BYTE_ORDER == BIG_ENDIAN
            static const uint32_t mask[4] = {
    
    
                0x00000000, 0xffffff00, 0xffff0000, 0xff000000
            };
#endif
#if BYTE_ORDER == LITTLE_ENDIAN
            static const uint32_t mask[4] = {
    
    
                0x00000000, 0x00ffffff, 0x0000ffff, 0x000000ff
            };
#endif
            *reinterpret_cast<uint32_t*>(data+padded-4) &= mask[padded-len];
        }
        finishWrite(padded);
        return data;
    }
    // 容量不够,扩容后重新写入
    status_t err = growData(padded);
    if (err == NO_ERROR) goto restart_write;
    return nullptr;
}

// 计算pad_size
#define PAD_SIZE_UNSAFE(s) (((s) + 3) & ~3UL)
static size_t pad_size(size_t s) {
    
    
    return PAD_SIZE_UNSAFE(s);
}

3.4写入String数据

可以知道也是和上面相似的步骤,先“对齐”,然后写入数据,分配内存。

// frameworks\base\core\jni\android_os_Parcel.cpp
static void android_os_Parcel_writeString16(JNIEnv *env, jclass clazz, jlong nativePtr,
        jstring val) {
    
    
    Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
    if (parcel != nullptr) {
    
    
        status_t err = NO_ERROR;
        if (val) {
    
    
            const size_t len = env->GetStringLength(val);
            const size_t allocLen = len * sizeof(char16_t);
            err = parcel->writeInt32(len);// “对齐”
            char *data = reinterpret_cast<char*>(parcel->writeInplace(allocLen + sizeof(char16_t))); // 写入数据
            if (data != nullptr) {
    
    
                // 分配内存
                env->GetStringRegion(val, 0, len, reinterpret_cast<jchar*>(data));
                *reinterpret_cast<char16_t*>(data + allocLen) = 0;
            } else {
    
    
                err = NO_MEMORY;
            }
        } else {
    
    
            err = parcel->writeString16(nullptr, 0);
        }
	// 异常情况
        if (err != NO_ERROR) {
    
    
            signalExceptionForError(env, clazz, err);
        }
    }
}

4.Parcel读取数据源码分析

上面aidl生成的代码中通过从Parcel中读取数据,代码如下:

data.enforceInterface(descriptor); // 获取IBinder接口标志
java.lang.String _arg0 = data.readString(); // 读取数据

4.1获取IBinder接口标识符

// frameworks\base\core\jni\android_os_Parcel.cpp
static void android_os_Parcel_enforceInterface(JNIEnv* env, jclass clazz, jlong nativePtr, jstring name)
{
    
    
    Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
    if (parcel != nullptr) {
    
    
        InterfaceDescriptorString descriptor(env, name);
        IPCThreadState* threadState = IPCThreadState::self();
        const int32_t oldPolicy = threadState->getStrictModePolicy();
        // 判断是否有效
        const bool isValid =
                parcel->enforceInterface(reinterpret_cast<const char16_t*>(descriptor.str()),
                                         descriptor.size(), threadState);
        if (isValid) {
    
    
            const int32_t newPolicy = threadState->getStrictModePolicy();
            if (oldPolicy != newPolicy) {
    
    
                set_dalvik_blockguard_policy(env, newPolicy);
            }
            return;
        }
    }
    // 异常情况
    jniThrowException(env, "java/lang/SecurityException",
            "Binder invocation to an incorrect interface");
}

与写入类似的,enforceInterface的读取步骤也与写入类似,最后调用了readString16Inplace()​。大致步骤包括读取Parcel容器长度、具体数据

// frameworks\native\libs\binder\Parcel.cpp
bool Parcel::enforceInterface(const char16_t* interface,
                              size_t len,
                              IPCThreadState* threadState) const
{
    
    
    // 读取接口标识符
    size_t parcel_interface_len;
    const char16_t* parcel_interface = readString16Inplace(&parcel_interface_len);
    // 比较获取的与传递过来的是否相同
    if (len == parcel_interface_len &&
            (!len || !memcmp(parcel_interface, interface, len * sizeof (char16_t)))) {
    
    
        return true;
    } else {
    
    
        return false;
    }
}

const char16_t* Parcel::readString16Inplace(size_t* outLen) const
{
    
    
    int32_t size = readInt32();
    if (size >= 0 && size < INT32_MAX) {
    
    
        *outLen = size;
        const char16_t* str = (const char16_t*)readInplace((size+1)*sizeof(char16_t));
        if (str != nullptr) {
    
    
            if (str[size] == u'\0') {
    
     // 结束位
                return str;
            }
        }
    }
    *outLen = 0;
    return nullptr;
}

详细步骤如下:

// frameworks\native\libs\binder\Parcel.cpp

// ------------------------- 步骤1:“对齐”,先读取长度 -------------------------
int32_t Parcel::readInt32() const
{
    
    
    return readAligned<int32_t>();
}

template<class T>
status_t Parcel::readAligned(T *pArg) const {
    
    
    static_assert(PAD_SIZE_UNSAFE(sizeof(T)) == sizeof(T));
    static_assert(std::is_trivially_copyable_v<T>);

    if ((mDataPos+sizeof(T)) <= mDataSize) {
    
    
        // 判断maybeKernelFields,先忽略
        memcpy(pArg, mData + mDataPos, sizeof(T)); // 读取数据
        mDataPos += sizeof(T); // 移动mDataPos
        return NO_ERROR;
    } else {
    
    
        return NOT_ENOUGH_DATA;
    }
}
// ------------------------- 步骤1:读取数据 -------------------------
const void* Parcel::readInplace(size_t len) const
{
    
    
    if (len > INT32_MAX) {
    
    
        return nullptr;
    }
    if ((mDataPos+pad_size(len)) >= mDataPos && (mDataPos+pad_size(len)) <= mDataSize
            && len <= pad_size(len)) {
    
     // 判断需要读取数据是否在Parcel中
        // 判断maybeKernelFields,先忽略      
        const void* data = mData+mDataPos;
        mDataPos += pad_size(len); // 移动mDataPos
        return data;
    }
    return nullptr;
}

4.2读取String数据

从下面代码中知道可上面获取IBinder接口标识符的步骤一样

// frameworks\base\core\jni\android_os_Parcel.cpp
static jstring android_os_Parcel_readString16(JNIEnv* env, jclass clazz, jlong nativePtr)
{
    
    
    Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
    if (parcel != NULL) {
    
    
        size_t len;
        const char16_t* str = parcel->readString16Inplace(&len);
        if (str) {
    
    
            return env->NewString(reinterpret_cast<const jchar*>(str), len);
        }
        return NULL;
    }
    return NULL;
}

// frameworks\native\libs\binder\Parcel.cpp
const char16_t* Parcel::readString16Inplace(size_t* outLen) const
{
    
    
    int32_t size = readInt32();
    if (size >= 0 && size < INT32_MAX) {
    
    
        *outLen = size;
        const char16_t* str = (const char16_t*)readInplace((size+1)*sizeof(char16_t));
        if (str != nullptr) {
    
    
            if (str[size] == u'\0') {
    
    
                return str;
            }
        }
    }
    *outLen = 0;
    return nullptr;
}

猜你喜欢

转载自blog.csdn.net/wangadping/article/details/128274995