欢迎转载,但请务必在明确位置注明文章出处! http://johnnyshieh.github.io/android/2016/12/07/serializable-vs-parcelable/
Parcelable 和 Serializable 都是实现序列化的接口,那么序列化具体是指什么呢?序列化是指把对象转换为可传输或可存储的状态,反序列化是把序列化后的内容转换为之前的对象。
下面分别讲述两者的设计初衷与具体实现的不同。
Serializable 是为了完成对象的持久化,方便保存到本地文件、数据库、网络流等,为对象提供标准的序列化与反序列化操作。
使用 Serializable 来实现序列化非常简单,只需要继承 Serializable 接口:
public class Person implements Serializable { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } }
但是经常看到有人实现 Serializable 接口时,还会添加静态变量 serialVersionUID
,这是Serializable对象的版本标识ID。如果像上面的一样没显示声明的话,默认这个ID会根据类的声明和它的成员通过hash计算出来。这个版本ID会在序列化过程中包含进去,并在反序列化过程中检测,如果本地的 serialVersionUID
与序列化数据中的不一致,反序列化就会失败并抛出 InvalidClassException
。
序列化 ID 在 Eclipse 下提供了两种生成策略,一个是固定的 1L,一个是随机生成一个不重复的 long 类型数据(实际上是使用 JDK 工具生成),在这里有一个建议,如果没有特殊需求,就是用默认的 1L 就可以,这样可以确保代码一致时反序列化成功。
上面介绍了如何实现序列化,那么具体序列化的规则是怎么样的,哪些可以序列化?哪些不会被序列化?
在序列化的过程中,虚拟机会调用对象类中的 writeObject
和 readObject
方法,在用户没有自定义的情况下,默认会调用 ObjectOutputStream 的 defaultWriteObject 方法以及 ObjectInputStream 的 defaultReadObject 方法。
所以如果需要序列化对象中有一些数据是敏感的话,如果密码字符串等,可以重写 writeObject
和 readObject
方法,在序列化过程中加密,反序列化过程中解密。
private void writeObject(java.io.ObjectOutputStream out) throws IOException { // write 'this' to 'out'... } private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException { // populate the fields of 'this' from the data in 'in'... }
另外需要注意的是:静态变量和 transient
关键字标记的变量不会序列化保存。静态变量不是对象的状态,所以不会参与序列化过程。
子类需要实现了 Serializable,父类也要实现 Serializable 接口,不然在默认序列化过程中会报java.io.NotSerializableException异常。
Android设计 Parcelable 的初衷是因为 Serializable 效率过慢,为了在程序内不同组件间以及不同Android程序间(AIDL)高效的传输数据而设计,这些数据仅在内存中存在,Parcelable 是通过IBinder通信的消息的载体。
public class MyParcelable implements Parcelable { private int mData; private Parcelable mParcel; public int describeContents() { return 0; } // 写数据进行保存 public void writeToParcel(Parcel out, int flags) { out.writeInt(mData); out.writeParcelable(mParcel, 0); } // 用来创建自定义的Parcelable的对象 public static final Parcelable.Creator<MyParcelable> CREATOR = new Parcelable.Creator<MyParcelable>() { public MyParcelable createFromParcel(Parcel in) { return new MyParcelable(in); } public MyParcelable[] newArray(int size) { return new MyParcelable[size]; } }; // 读数据进行恢复 private MyParcelable(Parcel in) { mData = in.readInt(); mParcel = in.readParcelable(Thead.currentThread().getContextClassLoader()); } }
下面是摘自任玉刚的《Android开发艺术探索》书中的 Parcelable 的方法说明:
方法 | 功能 | 标记位 |
---|---|---|
createFromParcel(Parcel in) | 从序列化后的对象中创建原始对象 | |
newArray(int size) | 创建指定长度的原始对象数组 | |
MyParcelable(Parcel in) | 从序列化后的对象中创建原始对象 | |
writeToParcel(Parcel out, int flags) | 将当前对象写入序列化结构中,flags有两种值 0 和 1。为1是表示当前对象需要作为返回值返回 | PARCELABLE_WRITE_RETURN_VALUE |
describeContents() | 返回当前对象的内容描述。如果含有文件描述符,返回1,否则返回0 | CONTENTS_FILE_DESCRIPTOR |
实现 Parcelable 接口的方式比 Serializable 麻烦一些,不过推荐大家在 Android Studio中下载Android Parcelable code generator。 Mac版的AS方法为 Preferences -> Plugins -> Browser repositories… -> 搜索Parcelable就可以看到这个插件了。 使用方法为Command + N调出Generate弹框,选择Parcelable即可。
Android系统本身中的很多类都实现了 Parcelable 接口,比如Intent、Bundle、Bitmap、ContentValue等。
编码上实现 Parcelable 接口更麻烦一点。
效率上 Parcelable 的速度比 Serializable 高十倍以上,因为 Serializable 序列化过程使用了反射,需要大量的I/O操作。
所以 Parcelable 主要用在内存序列化上,如Android组件之间通信,效率快。而 Serializable 更适合用于数据持久化,如本地保存或网络传输时,Parcelable 在这中情况过程会稍显复杂,且Android不同版本 Parcelable 可能不同。