Parcelable如何实现数据存取丝毫不差

53 阅读4分钟

用一个完整的物流故事,带你深入理解Parcelable的装箱、运输、拆箱全流程,特别是​​如何精准控制包裹大小(size)和货架指针(内存位置)​​,保证每个字段准确无误地抵达目的地!


📦 ​​第一章:接单!Parcelable快递公司的精密物流系统​

​客户(你的数据对象)​​:一个User包裹,内含name(字符串)、age(整型)、isVIP(布尔值)三个宝贝,还有个嵌套包裹Address(另一个Parcelable对象)。

​物流工具​​:

  • Parcel货柜​​:一个​​线性、二进制、带智能指针的集装箱​​(底层是共享内存区) 。

  • CREATOR拆包手册​​:每个包裹专属的“重建说明书”,告诉仓库如何组装零件 。

​核心规则​​:

⚠️ ​​“写入顺序 == 读取顺序”​​!这是物流链不出错的​​生死线​​ 。


🚚 ​​第二章:装箱!序列化的精准操作(writeToParcel())​

想象你是个严格按SOP操作的打包员:

  1. ​领取空货柜​​:调用Parcel.obtain()申请一个空集装箱(底层从内存池复用,减少开销) 。

  2. ​按清单顺序装箱​​:

    dest.writeString(name);  // 位置1:写入字符串(自动记录长度+内容)  
    dest.writeInt(age);       // 位置2:写入4字节整数  
    dest.writeBoolean(isVIP); // 位置3:写入1字节布尔值  
    dest.writeParcelable(address, 0); // 位置4:嵌套包裹!递归调用address的打包流程
    
  3. ​指针如何移动?​

    • 每写入一个数据,​​货柜指针mDataPos自动向后移动对应字节数​​(如writeInt()移动4字节) 。

    • 比如写入"小明"(UTF-8占6字节),指针从0→6;再写入age=30(4字节),指针移到10📍。

  4. ​Size如何计算?​

    • 最终size = mDataPos,即指针当前位置就是总数据长度。

    • 系统自动追踪,无需手动计算!


📬 ​​第三章:拆箱!反序列化的零误差重建(CREATOR)​

包裹抵达目标仓库(新进程),拆包员手持User.CREATOR手册:

  1. ​按相同顺序取货​​:

    
    String name = in.readString();  // 从位置1读:先读长度,再读内容  
    int age = in.readInt();         // 位置2:读4字节  
    boolean isVIP = in.readBoolean();// 位置3:读1字节  
    Address address = in.readParcelable(Address.class.getClassLoader()); // 位置4:递归拆嵌套包[7](@ref)  
    
  2. ​指针同步移动​​:

    • 每次readXxx(),​​指针自动向后移动写入时的字节数​​。
    • 若顺序错:readInt()读了字符串的位置 → 数据错乱崩溃💥!
  3. ​为何指针不出错?​

    • 写入和读取​​严格共享同一套移动逻辑​​,就像两仓库用同一张货架地图。

🔧 ​​第四章:仓库黑科技!Parcel的底层物流网​

  1. ​共享内存仓库(Binder驱动)​

    • 所有货柜Parcel数据通过​​Binder驱动​​直接在进程间传输,无需复制大文件(零拷贝优化) 。

    • 就像两个仓库共用同一片仓储区,快递员只传递“钥匙”(内存指针)。

  2. ​智能指针管理(mDataPos & mDataSize)​

    指针类型作用
    mDataPos​当前读写位置​​,每次操作后自动更新📍
    mDataSize​货柜总容量​​,写入时动态扩容(如初始4KB,超限自动翻倍)2
    mDataCapacity当前最大容量,避免频繁扩容
  3. ​嵌套包裹处理(递归指针)​

    • 写入writeParcelable()时:先写入​​类名标记​​(如"com.example.Address"),再写入对象数据 。

    • 读取readParcelable()时:先读类名→用ClassLoader加载类→再递归调用其CREATOR重建 。


⚠️ ​​第五章:物流事故预警!新手避坑指南​

  1. ​顺序错位​​ → ​​货不对板​

    // 错误示范:写是name→age,读变成age→name  
    dest.writeString(name); dest.writeInt(age);  // 写入  
    in.readInt(); in.readString();                // 读取 → 年龄变名字!  
    

    ​✅ 正确做法​​:用Android Studio插件自动生成代码(如kotlin-parcelize),机器保证顺序。

  2. ​忘记回收货柜​​ → ​​内存泄漏​

    Parcel parcel = Parcel.obtain();  
    // ...操作完毕必须归还!  
    parcel.recycle(); // 放回内存池复用[2,8](@ref)  
    
  3. ​跨进程传递大文件​​ → ​​货柜爆炸​​(TransactionTooLargeException

    • 解决方案:传递文件路径或URI,而非文件本身 。

🏆 ​​终极总结:Parcelable物流公司的核心优势​

​传统物流(Serializable)​​Parcelable智能物流​
靠反射扫描货物(性能差🚫)按清单直接装箱(无反射✅)
货物经多次转运(I/O操作↩️)仓库直送(内存拷贝⚡)
临时包装袋多(GC垃圾♻️)精简包装(低内存占用📉)
适合长期仓储(磁盘存储✅)仅限同城闪送(内存专用📦)

✨ ​​一句话口诀​​:
​“顺序一致指针对,CREATOR拆包不崩溃;
内存仓库速度快,大件走货会累赘!”​

下次你的数据要跨进程“闪送”,记得呼叫​​Parcelable快递公司​​——您的专属二进制物流专家!🚛💨