Earth Guardian

You are not LATE!You are not EARLY!

0%

Serializable 和 Parcelable

基本概念

序列化和反序列化

序列化和反序列化是指:将一个实例对象编码成字节流,并从字节流编码中重新构建对象实例的能力。

  • 序列化
    将一个对象编码成字节流。
  • 反序列化
    从一个字节流中读出一个对象实例。

反序列化是按照序列化写入的顺序读取的。

对象变量类型

  • 序列化对象的时候只是针对变量进行序列化,不针对方法进行序列化
  • 静态成员 static 变量属于类不属于对象,所以不参与序列化过程
  • transient 关键字标记的成员变量不参与序列化过程

两种方法

  • Java 提供的 Serializable
  • Android 提供的 Parcelable

Serializable 接口

Serializable 接口是 Java 提供的一个序列化接口,它是一个空接口,为对象提供标准的序列化和反序列化操作。使用 Serializable 来实现的对象的序列化相当简单,只需要在类中指定 serialVersionUID 标识即可自动实现默认的序列化过程。

serialVersionUID

  • 概念
    Java 的序列化机制是通过在运行时判断类的 serialVersionUID 来验证版本一致性的。在进行反序列化时,JVM 会把传来的字节流中的 serialVersionUID 与本地相应实体(类)的 serialVersionUID 进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常。

  • 根据类名、接口名、成员方法及属性等来生成一个 64 位的哈希字段。默认值是 1L ,但是 Android 强烈建议显示指定一个值。
  • 生成方法
    Android Studio 自动生成 serialVersionUID 的方法:
    打开 Settings,切换到 Editor->Inspections->Java->Serialization issues,找到 Serializationzable class without ‘serialVersionUID’,将其勾选即可。或者直接搜索 serialVersionUID
    鼠标放在 Serializable 接口的类名上, Android Studio 的智能提示(智能检查),就会多出了 Add ‘serialVersionUID’ field,完毕!

示例类

MyDataSerializable 自定义可以序列化的类,内部支持集合,但是静态变量和 tansient 申明的变量是无法序列化的。

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
public class MyDataSerializable implements Serializable {

private static final long serialVersionUID = -6385208676051817341L;

public String name;
public int age;
// 支持集合
public List<Book> bookList;

// 静态变量
public static int static_field;
// transien 申明的变量
public transient int transient_filed;

public static class Book implements Serializable{
private static final long serialVersionUID = -4088466130897610219L;
public String name;
public long price;

public Book(String name, long price){
...
}

public String toString(){
...
}
}

public MyDataSerializable(String name, int age){
...
}

public String toString(){
...
}
}

测试代码

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
private void testSerializable(){
MyDataSerializable myDataSerializable = initDataSerializable();
Log.d(TAG, "testSerializable: myDataSerializable = " +
myDataSerializable);

byte[] bytes = serializeData(myDataSerializable);
// 改变类中的静态变量的值
myDataSerializable.static_field = 10002;

MyDataSerializable deSerialData = deserializeBytes(bytes);
Log.d(TAG, "testSerializable: deSerialData = " + deSerialData);
}

private MyDataSerializable initDataSerializable(){
MyDataSerializable myDataSerializable =
new MyDataSerializable("kobe", 36);
MyDataSerializable.Book book1 = new MyDataSerializable.Book("abc", 13);
MyDataSerializable.Book book2 = new MyDataSerializable.Book("xyz", 56);
myDataSerializable.bookList.add(book1);
myDataSerializable.bookList.add(book2);
return myDataSerializable;
}

private byte[] serializeData(MyDataSerializable myDataSerializable){
byte[] bytes = null;
try {
ByteArrayOutputStream byteArrayOutputStream =
new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream =
new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(myDataSerializable);
objectOutputStream.flush();
objectOutputStream.close();
bytes = byteArrayOutputStream.toByteArray();
}catch (IOException e){
Log.e(TAG, "serializeData: e = ", e);
}

return bytes;
}

private MyDataSerializable deserializeBytes(byte[] bytes){
MyDataSerializable data = null;
try {
ByteArrayInputStream byteArrayInputStream =
new ByteArrayInputStream(bytes);
ObjectInputStream objectInputStream =
new ObjectInputStream(byteArrayInputStream);
data = (MyDataSerializable) objectInputStream.readObject();
}catch (Exception e){
Log.e(TAG, "deserializeBytes: e = ", e);
}
return data;
}

结果分析

1
2
3
4
5
6
11:32: 6536/com.yD/:ShowIPCActivity:: testSerializable: myDataSerializable = 
name: kobe, age: 36, book.name: abc, book.price: 13, book.name: xyz,
book.price: 56, static_field: 10001, transient_filed: 20001
11:32: 6536/com.yD/:ShowIPCActivity:: testSerializable: deSerialData =
name: kobe, age: 36, book.name: abc, book.price: 13, book.name: xyz,
book.price: 56, static_field: 10002, transient_filed: 0

Log 中可以看出:

  • transient 关键字
    申明的变量不能序列化,反序列化的结果为 0
  • static 变量
    定义为 static 的变量,是属于类的,全局的,实际上并不参与序列化。所以读取到的 static 变量值是全局数据区中的值,并不是反序列化产生的值,本例中表示改变后的值。

Parcelable 接口

Parcelable 接口是 Android SDK 提供的一种专门用于 Android 应用中对象的序列化和反序列化的方式,相比于 Seriablizable 具有更好的性能。实现 Parcelable 接口的对象就可以实现序列化并可以通过IntentBinder 传递。
序列化的对象可以包含:基本数据类型,BitmapListMapIBinder等,以及所有已经实现 Parcelable 的类。

实现及重写

实现 Parcelable 的类, Android Studio 会给出需要实现和重写的方法模板:

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
public static class Test implements Parcelable{
// 空构造方法
public Test(){
}

public Test(Parcel in) {
readFromParcel(in);
}

// 序列化
@Override
public void writeToParcel(Parcel dest, int flags) {
}

// 反序列化
public void readFromParcel(Parcel in){
}

@Override
public int describeContents() {
return 0;
}

public static final Creator<Test> CREATOR = new Creator<Test>() {
// 反序列化
@Override
public Test createFromParcel(Parcel in) {
return new Test(in);
}

@Override
public Test[] newArray(int size) {
return new Test[size];
}
};
}
  • 序列化
    writeToParcel 表示序列化的过程,将变量都写入 Parcel
  • 反序列化
    readFromParcel 表示反序列化的过程,从 Parcel 中读取并生成类对象。
  • describeContents
    该方法通常直接返回 0 。Parcelable 对象在扁平化中,如果包含文件描述符,则该值必须返回 CONTENTS_FILE_DESCRIPTOR 。 参考 Parcelable API

这里“空构造方法”和 readFromParcel 两个都不是必须的。反序列化也可以在 createFromParcel 中直接反序列化;但是 AIDL 中反序列化时,必须定义“空构造方法”和 readFromParcel 才能成功反序列化。所以我们最好在自定义 Parcelable 中默认实现这两个方法养成好习惯。

反序列化是按照序列化的顺序读取的,所以 readFromParcel 反序列化读取变量的值时,一定要按照 writeToParcel 写入的顺序读取。

示例类

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

public static final int ARRAY_SIZE = 3;
private String name;
// 可以是数组,注意写入和读取数组初始化的大小必须一致
private int[] arrays = new int[ARRAY_SIZE];
// 可以包含 List 以及已经实现了 Parcelable 的类
private List<Book> bookList;

public static class Book implements Parcelable{
...
}

public MyDataParcelable(String name, int[] arrays) {
this.name = name;
this.arrays = arrays;
bookList = new ArrayList<Book>();
}

// 空构造方法
public MyDataParcelable(){

}

public MyDataParcelable(Parcel in){
readFromParcel(in);
}

...

// 序列化
@Override
public void writeToParcel(Parcel dest, int flags) {
// 注意写入顺序
dest.writeString(name);
dest.writeIntArray(arrays);
dest.writeList(bookList);
}

// 反序列化
public void readFromParcel(Parcel in){
// 注意读取顺序,严格按照写入顺序读取
this.name = in.readString();
// 读取数组的大小也必须和写入的一致
this.arrays = new int[ARRAY_SIZE];
in.readIntArray(arrays);
this.bookList = new ArrayList<Book>();
in.readList(bookList, Book.class.getClassLoader());
}

@Override
public int describeContents() {
return 0;
}

public static final Creator<MyDataParcelable> CREATOR =
new Creator<MyDataParcelable>() {

@Override
public MyDataParcelable createFromParcel(Parcel in) {
return new MyDataParcelable(in);
}

@Override
public MyDataParcelable[] newArray(int size) {
return new MyDataParcelable[size];
}
};

public String toString(){
...
}
}

测试代码

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
// Activity1 中初始化 MyDataParcelable, 启动 Activity2
// 并将 MyDataParcelable 的对象传递过去
private void testParcelable(){
int[] arrays = new int[]{1, 2, 3};
MyDataParcelable myDataParcelable =
new MyDataParcelable("parcelable", arrays);
MyDataParcelable.Book book1 = new MyDataParcelable.Book("xiyouji", 20);
MyDataParcelable.Book book2 = new MyDataParcelable.Book("sanguo", 30);
myDataParcelable.addBook(book1);
myDataParcelable.addBook(book2);

Log.d(TAG, "testParcelable: myDataParcelable = " + myDataParcelable);
Intent intent = new Intent(ShowIPCActivity.this, Main2Activity.class);
intent.putExtra("PARCELABLE_DATA", myDataParcelable);
startActivity(intent);
}

// Activity2 中收到 Intent 后,获取 MyDataParcelable 数据
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);

Intent intent = getIntent();
if(intent != null){
MyDataParcelable myDataParcelable =
intent.getParcelableExtra("PARCELABLE_DATA");
Log.d(TAG, "onCreate: intent, myDataParcelable = " +
myDataParcelable);
}
}

结果分析

从结果上看,复杂数据对象 MyDataParcelable 成功的被序列化后传递出去,接收后能正常的反序列化解析出来。

1
2
3
4
5
6
16:02: 11509/com.yD/:ShowIPCActivity:: testParcelable: myDataParcelable = 
name: parcelable, arrays: 1 2 3 , book.name: xiyouji, book.price: 20,
book.name: sanguo, book.price: 30
16:02: 11509/com.yD/:Main2Activity:: onCreate: intent, myDataParcelable =
name: parcelable, arrays: 1 2 3 , book.name: xiyouji, book.price: 20,
book.name: sanguo, book.price: 30

对比

  • Serializable
    Java 中的序列化接口,使用起来简单但是开销大,序列化和反序列化需要大量的 I/O 操作。主要用于序列化到存储设备或者序列化后通过网络传输。
  • Parcelable
    Android 提供的标准序列化接口,因此更适合 Android 平台,缺点是使用稍微麻烦,但是效率高;主要用于内存序列化。

参考文档

  1. http://www.cnblogs.com/yezhennan/p/5527506.html
  2. http://www.developerphil.com/parcelable-vs-serializable/
  3. http://www.jianshu.com/p/fcc59fb523b6
  4. http://www.cnblogs.com/xyczero/p/4021245.html
  5. http://www.linuxidc.com/Linux/2015-03/115270.htm