Android序列化

Android实现序列化有两种方式,SerializableParcelable

Serializable接口

使用示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class User implements Serializable {
private static final long serialVersionUID = 519067123721295773L;

public int userId;
public String userName;
public boolean isMale;

public User() {
}

public User(int userId, String userName, boolean isMale) {
this.userId = userId;
this.userName = userName;
this.isMale = isMale;
}
}

Serializable是Java提供的一个序列化接口(空接口),为对象提供标准的序列化和反序列化操作。

使用Serializable接口来实现序列化,只需要一个类去实现Serializable接口并声明一个serialVersionUID即可实现序列化。

serialVersionUID主要是辅助序列化和反序列化的过程的,反序列化的时候,如果当前类的serialVersionUID和序列化的类的serialVersionUID值不一样,那么反序列化就会失败。

我们可以手动的设置serialVersionUID的值比如1L,也可以根据当前类的结构生成hash值赋值给serialVersionUID。当然我们也可以不指定serialVersionUID,如果不手动指定serialVersionUID的值,反序列化时当前类有所改变(比如增删了某些成员变量),那么系统就会重新计算当前类的hash值并赋值给serialVersionUID。这个时候当前类的serialVersionUID就和序列化数据中的serialVersionUID不一致,导致反序列化失败,程序就出现crash。

为了最大程度的进行反序列化,恢复数据,我们最好手动的为序列化的对象指定serialVersionUID。

需要注意的是,静态成员变量属于类不属于对象,不参与序列化过程,其次transient关键字标记的成员变量不参与序列化过程。

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
public class User implements Parcelable {

public int userId;
public String userName;
public boolean isMale;

public Book book;

public User() {
}

public User(int userId, String userName, boolean isMale) {
this.userId = userId;
this.userName = userName;
this.isMale = isMale;
}

public int describeContents() {
return 0;
}

public void writeToParcel(Parcel out, int flags) {
out.writeInt(userId);
out.writeString(userName);
out.writeInt(isMale ? 1 : 0);
out.writeParcelable(book, 0);
}

public static final Creator<User> CREATOR = new Creator<User>() {
public User createFromParcel(Parcel in) {
return new User(in);
}

public User[] newArray(int size) {
return new User[size];
}
};

private User(Parcel in) {
userId = in.readInt();
userName = in.readString();
isMale = in.readInt() == 1;
book = in
.readParcelable(Thread.currentThread().getContextClassLoader());
}

@Override
public String toString() {
return String.format(
"User:{userId:%s, userName:%s, isMale:%s}, with child:{%s}",
userId, userName, isMale, book);
}

}

Parcelable内部包装了可序列化的数据,可以在Binder中自由的传输。

在序列化过程中需要实现的功能有,序列化,反序列化和内容描述。

序列化功能由writeToParcel方法完成,最终是通过Parcel的一系列writer方法来完成。

1
2
3
4
5
@Override
public void writeToParcel(Parcel out, int flags) {
out.writeInt(code);
out.writeString(name);
}

反序列化功能由CREATOR来完成,其内部表明了如何创建序列化对象和数组,通过Parcel的一系列read方法来完成。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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];
}
};

protected Book(Parcel in) {
code = in.readInt();
name = in.readString();
}

内容描述功能由describeContents方法完成,几乎所有情况下都应该返回0,仅当当前对象中存在文件描述符时返回1。

1
2
3
public int describeContents() {
return 0;
}

Android提供了许多实现了Parcelable接口的类,比如Intent,Bundle,Bitmap等。List和Map也可以序列化,前提是他们里面的对象都是可序列化的。

两种方式比较

上述的两种序列化接口都有各自不同的优缺点,我们在实际使用时需根据不同情况而定。

Serializable是Java的序列化接口,使用简单但开销大,序列化和反序列化过程需要大量I/O操作,同时在序列化的时候会产生大量的临时变量,从而引起频繁的GC。

Parcelable是Android中的序列化方式,适合在Android平台使用,效率高但是使用麻烦。Parcelable 主要在内存序列化上,如序列化对象在网络中传递对象或序列化在进程间传递对象,这些时候更推荐使用Parcelable接口。但Parcelable有个明显的缺点:不能能使用在要将数据存储在磁盘上的情况(如:永久性保存对象,保存对象的字节序列到本地文件中),因为Parcel本质上为了更好的实现对象在IPC间传递,并不是一个通用的序列化机制,当改变任何Parcel中数据的底层实现都可能导致之前的数据不可读取,所以此时还是建议使用Serializable 。

主要参考自:

浅谈Android序列化
《Android开发艺术探索》

当前网速较慢或者你使用的浏览器不支持博客特定功能,请尝试刷新或换用Chrome、Firefox等现代浏览器