基本概念
序列化和反序列化
序列化和反序列化是指:将一个实例对象编码成字节流,并从字节流编码中重新构建对象实例的能力。
- 序列化
将一个对象编码成字节流。
- 反序列化
从一个字节流中读出一个对象实例。
反序列化是按照序列化写入的顺序读取的。
对象变量类型
- 序列化对象的时候只是针对变量进行序列化,不针对方法进行序列化
- 静态成员
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; 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
接口的对象就可以实现序列化并可以通过Intent
和 Binder
传递。
序列化的对象可以包含:基本数据类型,Bitmap
,List
,Map
,IBinder
等,以及所有已经实现 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]; 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
|
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); }
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
平台,缺点是使用稍微麻烦,但是效率高;主要用于内存序列化。
参考文档
- http://www.cnblogs.com/yezhennan/p/5527506.html
- http://www.developerphil.com/parcelable-vs-serializable/
- http://www.jianshu.com/p/fcc59fb523b6
- http://www.cnblogs.com/xyczero/p/4021245.html
- http://www.linuxidc.com/Linux/2015-03/115270.htm