转载

Parcelable vs Serializable

欢迎转载,但请务必在明确位置注明文章出处! http://johnnyshieh.github.io/android/2016/12/07/serializable-vs-parcelable/

前言

Parcelable 和 Serializable 都是实现序列化的接口,那么序列化具体是指什么呢?序列化是指把对象转换为可传输或可存储的状态,反序列化是把序列化后的内容转换为之前的对象。

下面分别讲述两者的设计初衷与具体实现的不同。

1. 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 就可以,这样可以确保代码一致时反序列化成功。

序列化规则

上面介绍了如何实现序列化,那么具体序列化的规则是怎么样的,哪些可以序列化?哪些不会被序列化?

在序列化的过程中,虚拟机会调用对象类中的 writeObjectreadObject 方法,在用户没有自定义的情况下,默认会调用 ObjectOutputStream 的 defaultWriteObject 方法以及 ObjectInputStream 的 defaultReadObject 方法。

所以如果需要序列化对象中有一些数据是敏感的话,如果密码字符串等,可以重写 writeObjectreadObject 方法,在序列化过程中加密,反序列化过程中解密。

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异常。

2. Parcelable 接口

设计初衷

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 可能不同。

原文  http://johnnyshieh.github.io/android/2016/12/07/serializable-vs-parcelable/
正文到此结束
Loading...