Android-Parcelable

Parcelable接口

Parcelable是android.io包下的接口,是Android中推荐使用的序列化接口。

只要实现Parcelable接口的类,就可以使用Parcel进行序列化和反序列化操作。

实现Pacelable接口

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
public class Book implements Parcelable {
private String name;
private int price;
public String getName() {
return name;
}
public int getPrice() {
return price;
}
public void setName(String name) {
this.name = name;
}
public void setPrice(int price) {
this.price = price;
}
public Book() {
}
protected Book(Parcel in) {
name = in.readString();
price = in.readInt();
}
// 固定写法,在Pacel进行反序列化的时候,会通过反射调用CREATOR的方法。
public static final Creator<Book> CREATOR = new Creator<Book>() {
@Override
public Book createFromParcel(Parcel in) {
return new Book(in);
}
@Override
public Book[] newArray(int size) {
return new Book[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeInt(price);
}
}

Parcel是什么

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

Parcel的读写源码

Parcel.writeParcelable

1
2
3
4
5
6
public final void writeParcelable(Parcelable p, int parcelableFlags) {
// 先向流中写入p的ClassName
writeParcelableCreator(p);
// 然后直接调用p的writeToParcel,这个方法也就是我们自己重写的
p.writeToParcel(this, parcelableFlags);
}

这里需要注意的是,每一个Parcelable对象在写入流之前,都会在前面首先写入这个对象的ClassName,主要是方便后面读的时候,能够知道是哪个类,感觉这个地方还是做的比较粗糙,在Serializable中对应一个序列化类的信息刻画比这简单的一个类名要靠谱得多,所以官方文档上才会说,如果你想进行持久化存储,那么Parcelable不是你的菜,道理很简单,这里不会有任何版本的概念,只要你的类名不改,旧版本的数据就可以被新版本的class进行反序列化,然而class里面的属性可能已经完全不一样了。

Parcel.readParcelable

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public final <T extends Parcelable> T readParcelable(ClassLoader loader) {
// 1. 首先会调用到readParcelableCreator,通过反射读取我们类中定义的CREATOR
Parcelable.Creator<?> creator = readParcelableCreator(loader);
if (creator == null) {
return null;
}
if (creator instanceof Parcelable.ClassLoaderCreator<?>) {
Parcelable.ClassLoaderCreator<?> classLoaderCreator =
(Parcelable.ClassLoaderCreator<?>) creator;
return (T) classLoaderCreator.createFromParcel(this, loader);
}
// 2. 然后直接调用CREATOR.createFromParcel(parcel)
return (T) creator.createFromParcel(this);
}
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
// 首先会调用到readParcelableCreator,通过反射读取我们类中定义的CREATOR
public final Parcelable.Creator<?> readParcelableCreator(ClassLoader loader) {
// 首先把类名读取出来
String name = readString();
Parcelable.Creator<?> creator;
// mCreators做了一下缓存,如果之前某个classloader把一个parcelable的Creator获取过
// 那么就不需要通过反射去查找了
synchronized (mCreators) {
HashMap<String,Parcelable.Creator<?>> map = mCreators.get(loader);
if (map == null) {
map = new HashMap<>();
mCreators.put(loader, map);
}
creator = map.get(name);
if (creator == null) {
try {
// If loader == null, explicitly emulate Class.forName(String) "caller
// classloader" behavior.
ClassLoader parcelableClassLoader =
(loader == null ? getClass().getClassLoader() : loader);
// 加载我们自己实现Parcelable接口的类
Class<?> parcelableClass = Class.forName(name, false,
parcelableClassLoader);
Field f = parcelableClass.getField("CREATOR");
Class<?> creatorType = f.getType();
creator = (Parcelable.Creator<?>) f.get(null);
}
catch (Exception e) {
// catch exception
}
if (creator == null) {
throw new BadParcelableException("Parcelable protocol requires a "
+ "non-null Parcelable.Creator object called "
+ "CREATOR on class " + name);
}
map.put(name, creator);
}
}
return creator;
}

Serializable接口

Serializable是java.io包下的一个接口,是Java类进行序列化和反序列化的标记接口。

一个类实现了Serializable接口,就可以被ObjectOutputStream和ObjectInputStream进行序列化和反序列化的操作。

被transient描述的属性或类的静态变量是不会被序列化的。

如果一个实现了Serializable接口的类,继承了另外一个类。那么这个类的父类,必须继承Serializable或者提供一个空构造方法。

反序列化产生的对象并不是通过构造器创造的,所以依赖构造器保证的约束条件在对象反序列化时都无法保证。比如一个设计成单利的类,如果能序列化,那么可以反序列化出N个对象。

序列化和反序列化中隐藏的方法

具体的方法修饰符,private protected public 都可以,序列化和反序列化都是通过反射来调用的。

private Object writeReplace() throws ObjectStreamException ObjectOutPutStream

在序列化一个对象时,会首先调用这个方法,此方法中可以替换原有序列化的对象。

private void writeObject(java.io.ObjectOutputStream out) throws IOException

调用ObjectOutputStream.defaultWriteObject()使用的是默认的序列化过程,在调用defaultWriteObject方法后,可以往out流中写入一些数据,在readObject中可以读取。

private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException

在writeObject中写入的数据,在此方法中需要以写入顺序读取,同时我们注意到这里已经把参数inputstream回调给我们了,我们可以在这个位置注册一个回调,可以在validateObject方法中检查这个反序列化对象是否合法。

private Object readResolve() throws ObjectStreamException

此方法中可以替换反序列化出来的对象。

实现Serializable接口

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
/**
* Person类除了writeReplace在序列化时用到,其他的黑科技方法都不会被调用
* 原因是我们在writeReplace中将序列化对象替换成了内部代理类,所以以后的序列化过程就是针对SerializableProxy
*/
class Person implements Serializable {
private static final long serialVersionUID = 1L;
public String desc;
public Person(String desc) {
this.desc = desc;
}
static class SerializableProxy implements Serializable {
private static final long serialVersionUID = 1L;
private String desc;
private SerializableProxy(Person s) {
this.desc = s.desc;
}
/**
* 在这里恢复外围类 注意看这里!!!最大的好处就是我们最后得到的外围类是通过构造器构建的!
*
* @return
*/
private Object readResolve() {
return new Person(desc);
}
}
/**
* 外围类直接替换成静态内部代理类作为真正的序列化对象
*
* @return
*/
private Object writeReplace() {
return new SerializableProxy(this);
}
/**
* 这里主要是为了防止攻击,任何以Persion声明的对象字节流都是流氓!!
* 因为我在writeReplace中已经把序列化的实例指向了SerializableProxy
*
* @param stream
* @throws InvalidObjectException
*/
private void readObject(ObjectInputStream stream) throws InvalidObjectException {
throw new InvalidObjectException("proxy requied!");
}
}

自定义Serializable序列化

ObjectOutputStream的defaultWriteObject和defaultReadObject,是通过反射的方式实现序列化和反序列化的。
这种默认方式,会导致效率上比Android的Parcelable慢。
但是如果通过自己实现writeObject和readObject,那么在效率上会比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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
public class TreeNode implements Serializable, Parcelable {
private static final long serialVersionUID = 1L;
public List<TreeNode> children;
public String string0;
public String string1;
public String string2;
public int int0;
public int int1;
public int int2;
public boolean boolean0;
public boolean boolean1;
public boolean boolean2;
public TreeNode() {
}
protected TreeNode(Parcel in) {
if (in.readByte() == 0x01) {
children = new ArrayList<TreeNode>();
in.readList(children, TreeNode.class.getClassLoader());
} else {
children = null;
}
string0 = in.readString();
string1 = in.readString();
string2 = in.readString();
int0 = in.readInt();
int1 = in.readInt();
int2 = in.readInt();
boolean0 = in.readByte() != 0x00;
boolean1 = in.readByte() != 0x00;
boolean2 = in.readByte() != 0x00;
}
private void writeObject(ObjectOutputStream out) throws IOException {
out.writeUTF(string0);
out.writeUTF(string1);
out.writeUTF(string2);
out.writeInt(int0);
out.writeInt(int1);
out.writeInt(int2);
out.writeBoolean(boolean0);
out.writeBoolean(boolean1);
out.writeBoolean(boolean2);
if (children != null) {
out.writeInt(children.size());
for (TreeNode child : children) {
child.writeObject(out);
}
} else {
out.writeInt(0);
}
}
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
string0 = in.readUTF();
string1 = in.readUTF();
string2 = in.readUTF();
int0 = in.readInt();
int1 = in.readInt();
int2 = in.readInt();
boolean0 = in.readBoolean();
boolean1 = in.readBoolean();
boolean2 = in.readBoolean();
int childCount = in.readInt();
if (childCount > 0) {
children = new ArrayList<TreeNode>(childCount);
for (int i = 0; i < childCount; i++) {
TreeNode child = new TreeNode();
child.readObject(in);
children.add(child);
}
}
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
if (children == null) {
dest.writeByte((byte) (0x00));
} else {
dest.writeByte((byte) (0x01));
dest.writeList(children);
}
dest.writeString(string0);
dest.writeString(string1);
dest.writeString(string2);
dest.writeInt(int0);
dest.writeInt(int1);
dest.writeInt(int2);
dest.writeByte((byte) (boolean0 ? 0x01 : 0x00));
dest.writeByte((byte) (boolean1 ? 0x01 : 0x00));
dest.writeByte((byte) (boolean2 ? 0x01 : 0x00));
}
public static final Parcelable.Creator<TreeNode> CREATOR = new Parcelable.Creator<TreeNode>() {
@Override
public TreeNode createFromParcel(Parcel in) {
return new TreeNode(in);
}
@Override
public TreeNode[] newArray(int size) {
return new TreeNode[size];
}
};
}