Android实现序列化有两种方式,Serializable 和Parcelable 。
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开发艺术探索》
上一篇:Binder的使用和上层原理
下一篇:多进程模式的运行机制