Parcel 简介

Parcel 翻译过来是打包的意思, 其实就是包装了我们需要传输的数据, 然后在 Binder 中传输, 也就是用于跨进程传输数据

简单来说,Parcel 提供了一套机制,可以将序列化之后的数据写入到一个共享内存中,其他进程通过 Parcel 可以从这块共享内存中读出字节流,并反序列化成对象

image.png

Parcelable 流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
class Demo() : Parcelable {
private var index: Int = 0
private var name: String = "name"
private var price: Double = 1.0
private var checked: Boolean = false
private var child: Child = Child()
private var users: MutableList<Child> = mutableListOf()

constructor(parcel: Parcel) : this() {
index = parcel.readInt()
name = parcel.readString() ?: ""
price = parcel.readDouble()
checked = if (VERSION.SDK_INT >= VERSION_CODES.Q) {
parcel.readBoolean()
} else {
parcel.readInt() == 1
}
child = Child.createFromParcel(parcel)

parcel.readList(users, Child::class.java.classLoader)
parcel.readTypedList(users, Child.CREATOR)
}

companion object CREATOR : Creator<Demo> {
override fun createFromParcel(parcel: Parcel): Demo {
return Demo(parcel)
}

override fun newArray(size: Int): Array<Demo?> {
return arrayOfNulls(size)
}
}

override fun describeContents(): Int {
return 0
}

override fun writeToParcel(dest: Parcel, flags: Int) {
dest.writeInt(index)
dest.writeString(name)
dest.writeDouble(price)
if (VERSION.SDK_INT >= VERSION_CODES.Q) {
dest.writeBoolean(checked)
} else {
dest.writeInt(if (checked) 1 else 0)
}

dest.writeParcelable(child, PARCELABLE_WRITE_RETURN_VALUE)

dest.writeList(users)
dest.writeTypedList(users)
}

}

描述

在写入和读取 Parcelable 对象时,Android 框架会检查 describeContents() 方法的返回值,并将其作为特性标志的一部分写入或读取到序列化数据中。这样,在接收端读取数据时,就可以根据该标志来确定如何反序列化该对象。[一般都传 0]

序列化

通过 writeToParcel 实现序列化,根据传入的参数 dest 写入数据

反序列化

反序列化需要定义一个 CREATOR 的变量, 也可以自己定义一个 (名字千万不能改), 通过匿名内部类实现 Parcelable 中的 Creator 的接口

特殊类型

1. Boolean 类型

1
2
3
4
// Android Q 以上提供 writeBoolean 方法,其实内部也是调用 writeInt 方法
public final void writeBoolean(boolean val) {
writeInt(val ? 1 : 0);
}

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 系统内进行,无法进行跨平台传输。

使用场景:

  • 适用于数据对象比较复杂,具有复杂嵌套关系的情况下。
  • 需要实现跨进程通信的情况下。