通俗版讲解:Parcel 是什么?
你可以把 Parcel 想象成一个「快递包裹」,它专门用来在 Android 不同进程之间传递数据。比如 App 要给系统服务发请求,或者两个 App 要通信,数据都得打包成 Parcel 的格式。
核心知识点(一句话版)
- 跨层协作
Java 层的 Parcel 只是个「空壳」,真正干活的都是 Native 层(C++)的代码,通过 JNI 调用。 - 对象池优化
频繁创建/销毁 Parcel 太费资源,所以系统用「缓存池」管理。比如池子里有 6 个现成的 Parcel,用完了就还回来,不够用再新建。 - 支持的数据类型
能打包基础类型(int 等)、数组、Bundle、Binder 对象等。但要注意:List/Map 这类容器如果没指定泛型,可能出问题。
关键流程拆解
1️⃣ 获取 Parcel 对象(借包裹)
-
操作:
Parcel.obtain() -
内部逻辑:
- 先看缓存池(sOwnedPool)有没有闲置的 Parcel,有就直接拿
- 没有就 new 一个,并让它的内部指针指向 Native 层新创建的 C++ Parcel 对象
2️⃣ 回收 Parcel(还包裹)
-
操作:
recycle() -
内部逻辑:
- 如果缓存池没满,就把 Parcel 放回去标记为「可复用」
- 如果池子满了,就彻底销毁(包括 Native 层的内存释放)
3️⃣ 数据读写(装货/卸货)
-
写 int:Java 的
writeInt()→ 调用 Native 的writeInt32() -
读 int:Java 的
readInt()→ 调用 Native 的readInt32() -
写 Binder(如 Service):
- Java 的 Binder 对象 → 通过 JNI 找到对应的 Native 层 Binder(JavaBBinder 或 BpBinder)
- 把 Native Binder 的指针写入 Parcel
-
读 Binder:
- 从 Parcel 读取出 Native Binder 指针 → 转成 Java 的 Binder 或 BinderProxy 对象
为什么这样设计?
- 性能:Native 层操作内存更高效,适合高频的 IPC 通信
- 复用:对象池减少频繁创建/销毁带来的开销
- 兼容性:Java 层提供友好的 API,底层用 C++ 实现复杂逻辑
举个栗子 🌰
假设你要通过快递(Binder)寄一本书(数据):
- 打包:用 Parcel 把书装好(writeInt、writeBinder)
- 寄送:Binder 驱动把包裹送到对方进程
- 拆包:对方用 Parcel 把书取出(readInt、readBinder)
- 回收:包裹盒子(Parcel)还到快递站,下次继续用