【Android】Parcel简介

本文详细介绍了Parcel的工作原理,包括数据的存储方式、内存对齐规则、内存数据写入方法及内存扩展策略。此外还探讨了Parcel与Binder之间的交互过程,尤其是在传递文件描述符时的具体实现细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


1. Parcel的原理是把数据按照类型和顺序写在内存中,取数据的时候,按照同样的顺序即可

2.存储数据的时候,以4字节为基本单位进行内存对齐

3.采用memcpy以及

 *reinterpret_cast<T*>(mData+mDataPos) = val; 的方式写内存数据
4.采用realloc的方式扩展内存大小
5.和binder纠缠在一起了,传送文件描述符的时候,对数据动了手脚,根据原fd在目标进程中创建了一个对应的fd,
由于是在底层(binder驱动)进行的操作,为了让服务端不作特殊操作,就把新的fd修改到了Parcel数据里传送到了服务端
 
由于读数据的时候,都是按照给定的大小去读,下面代码里的
 *reinterpret_cast<uint32_t*>(data+padded-4) &= mask[padded-len];
是可以去掉的,写在这里会让人难以理解,如果要初始化扩展出来的字节,直接将其memset为0即可,
写成这样到显得晦涩
void* Parcel::writeInplace(size_t len)
{
    if (len > INT32_MAX) {
        // don't accept size_t values which may have come from an
        // inadvertent conversion from a negative int.
        return NULL;
    }

    const size_t padded = pad_size(len);

    // sanity check for integer overflow
    if (mDataPos+padded < mDataPos) {
        return NULL;
    }

    if ((mDataPos+padded) <= mDataCapacity) {
restart_write:
        //printf("Writing %ld bytes, padded to %ld\n", len, padded);
        uint8_t* const data = mData+mDataPos;

        // Need to pad at end?
        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
            //printf("Applying pad mask: %p to %p\n", (void*)mask[padded-len],
            //    *reinterpret_cast<void**>(data+padded-4));
            *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 NULL;
}

### AndroidParcel 的使用方法 #### 序列化与反序列化的机制 `Parcel` 是一种高效的序列化工具,在 `Android` 平台被广泛应用于进程间通信 (IPC)[^2]。为了使自定义类能够支持这种序列化方式,开发者需要让其继承并实现 `Parcelable` 接口。 #### 实现 Parcelable 接口 要创建可序列化的对象,需遵循如下模式: 1. **声明 CREATOR 字段** 类中应包含静态字段 `CREATOR`,它实现了 `Parcelable.Creator<T>` 接口,负责实例化新对象。 2. **重写 write.Parcel 方法** 将对象的数据成员按顺序写入到 `Parcel` 对象里。 3. **提供公共构造函数读取 parcel 数据** 构造器接收一个 `Parcel` 参数来初始化当前对象的状态。 ```java public class MyObject implements Parcelable { private String name; private int age; public static final Creator<MyObject> CREATOR = new Creator<MyObject>() { @Override public MyObject createFromParcel(Parcel source) { return new MyObject(source); } @Override public MyObject[] newArray(int size) { return new MyObject[size]; } }; protected MyObject(Parcel in) { this.name = in.readString(); this.age = in.readInt(); } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(name); dest.writeInt(age); } @Override public int describeContents() { return 0; } } ``` #### 使用 Intent 或 Bundle 进行传输 一旦实现了上述逻辑,则可以通过 `Intent` 或者 `Bundle` 来携带此类对象: ```java // 创建一个新的MyObject实例 MyObject obj = new MyObject(); // 放置在Intent中 intent.putExtra("key", obj); // 获取传来的obj MyObject receivedObj = intent.getExtras().getParcelable("key"); ``` #### 注意事项 对于不同版本的 Android 系统,`Parcel` 内部的对象池管理有所差异;特别是从 Android 31 开始引入了一些新的变化[^1]。另外需要注意的是,在某些特定情况下(比如跨平台服务),可能会遇到字节对齐的问题,这可能导致数据丢失或损坏的情况发生[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值