Parcelable 原理
Parcel 简介
Parcel 翻译过来是打包的意思, 其实就是包装了我们需要传输的数据, 然后在 Binder 中传输, 也就是用于跨进程传输数据
简单来说,Parcel 提供了一套机制,可以将序列化之后的数据写入到一个共享内存中,其他进程通过 Parcel 可以从这块共享内存中读出字节流,并反序列化成对象
Parcelable 流程
1 | class Demo() : Parcelable { |
描述
在写入和读取 Parcelable 对象时,Android 框架会检查 describeContents() 方法的返回值,并将其作为特性标志的一部分写入或读取到序列化数据中。这样,在接收端读取数据时,就可以根据该标志来确定如何反序列化该对象。[一般都传 0]
序列化
通过 writeToParcel 实现序列化,根据传入的参数 dest 写入数据
反序列化
反序列化需要定义一个 CREATOR 的变量, 也可以自己定义一个 (名字千万不能改), 通过匿名内部类实现 Parcelable 中的 Creator 的接口
特殊类型
1. Boolean 类型
1 | // Android Q 以上提供 writeBoolean 方法,其实内部也是调用 writeInt 方法 |
2. Parcelable 类
dest.writeParcelable(child, 0)
注释中写道,序列化对象的时候传入要序列化的对象和一个flags, 这里的 flags 几乎都是0,除非标识当前对象需要作为返回值返回,不能立即释放资源
child = Child.createFromParcel(parcel)
通过直接调用 createFromParcel 读取对象
3. List 类
writeList
这些方法们把类的信息和数据都写入Parcel,以使将来能使用合适的类装载器重新构造类的实例,所以效率不高
writeTypedList
这些方法不会写入类的信息,取而代之的是:读取时必须能知道数据属于哪个类并传入正确的Parcelable.Creator来创建对象,而不是直接构造新对象。
更加高效的读写单个Parcelable对象的方法是:直接调用 Parcelable.writeToParcel() 和 Parcelable.Creator.createFromParcel()
readList
需要传入类加载器来读取,因为写入的时候传入了类的相关信息,如 parcel.readList(users, Child::class.java.classLoader)
获取类加载器主要有几种方式
getClass().getClassLoader();
Thread.currentThread().getContextClassLoader();
Child.class.getClassLoader();
readTypedList
需要使用类的CREATOR去获取,如 parcel.readTypedList(users, Child.CREATOR)
和 Serializable 区别
Parcelable 和 Serializable 都是实现序列化并且都可以用于 Intent 间传递数据
Serializable
优点:
- 使用起来比较简单,只需要实现 Serializable 接口并重写其中的方法即可。
- 支持对象图的序列化和反序列化,包含了对象中所有的属性信息。
- 序列化和反序列化的过程都由虚拟机自动完成,无需手动实现。
缺点:
- 序列化和反序列化的过程较为耗时,会对性能产生一定的影响。
- 反序列化过程中可能存在安全漏洞,因为外部攻击者可以修改序列化的数据,导致反序列化时发生异常或者执行不安全的代码。
使用场景:
- 适用于数据对象比较简单,没有复杂嵌套关系的情况下。
Parcelable
优点:
- 序列化和反序列化的过程比 Serializable 更加高效,因为 Parcelable 是在内存中进行序列化和反序列化的,而不是在 IO 流中进行。
- Parcelable 在序列化和反序列化过程中可以进行精细控制,能够指定要序列化的字段,可以避免无关数据的传输和浪费。
- Parcelable 支持进程间通信,可以用于 Android 中的跨进程通信。
缺点:
- 使用起来相对于 Serializable 更为繁琐,需要手动实现 Parcelable 接口。
- Parcelable 的序列化和反序列化只能在 Android 系统内进行,无法进行跨平台传输。
使用场景:
- 适用于数据对象比较复杂,具有复杂嵌套关系的情况下。
- 需要实现跨进程通信的情况下。