Android中两种序列化方式的比较Serializable和Parcelable

Serializable和Parcelable接口可以完成对象的序列化过程,当我们需要通过Intent和Binder传输数据时就需要使用者两种序列化方式。还有,我们需要对象持久化到存储设备或者通过网络传输给其他客户端,这个使用也需要使用Serializale来完成对象的序列化。在Android应用开发中,这两种方式都很常见,但两者方式并不相同。

1.Serializable接口

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

private static final long serialVersionUID=871238749032;

完整的对象序列化代码示例如下:

//Model
public class User implements Serializable{
    private static final long serialVersionUID=871238749032;

    public int userId;
    public String userName;
    public String password;
}

//序列化到本地
User user=new User(0,"[email protected]","123456");
ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream("user.obj"));
out.writeObject(user);
out.close;

//反序列化
ObjectInputStream in=new ObjectInputStream(new FileInputStream("user.obj"));
User user=(User)in.readObject();
in.close();

这种方式是Java提供的一种序列化方式,过程非常简单,甚至有些开发人员都不需要声明serialVersionUID也可以完成这个过程,但serialVersionUID到底需不需要指定呢?

需要!

Java API既然提供了这个serialVersionUID,那么它必定是有用的。这个serialVersionUID是用来辅助序列化和反序列化过程的,原则上序列化后的数据中的serialVersionUID只有和当前类的serialVersionUID相同才能够正常地被反序列化。

serialVersionUID的详细工作过程是这样的:序列化的时候系统会把当前类的serialVersionUID写入序列化的二进制文件中,当反序列化的时候系统会检测文件中的serialVersionUID是否和当前类的serialVersionUID一致,如果一致就说明序列化的类的版本和当前类的版本是相同的,这个时候可以成功反序列化;否则说明当前类和反序列化的类相比发生了某些变化,比如成员变量的数量、类型发生了变化,这个时候是无法正常反序列化的。

一般来说,我们应该手动指定serialVersionUID的值,比如1L,也可以让IDE根据当前类的结构自动去生成它的hash值,这样序列化和反序列化时两者的serialVersionUID是相同的,因此可以正常进行反序列化操作。如果不手动指定serialVersionUID的值反序列化时当前类有些改变,比如增加或者删除了某些成员变量,那么系统就会重新计算当前类的hash值并把它赋值给serialVersionUID,这个时候当前类的serialVersionUID就和反序列化数据中的serialVersionUID不一致,就会造成反序列化失败的结果。所以,手动指定serialVersionUID可以在很大程度上避免反序列化过程的失败。比如当版本升级后,我们可能删除了某个成员变量也可能增加了一些新的成员变量,这个时候我们的反序列化过程依然能够成功,程序仍然能够最大限度地回复数据;相反,如果不指定serialVersionUID的话,程序会发生Crash。

当然,我们还需要考虑一种情况,如果类结构发生了非城规改变,比如修改了类名,修改了成员变量的类型,这个时候尽管serialVersionUID验证通过了,但是反序列化过程仍然会失败,因为类的结构有了毁灭性的改变,根本无法从老版本的数据中还原出一个新的类结构的对象。

对于使用序列化还有两点需要注意:
1.静态成员变量属于类不属于对象,所以不参与序列化过程
2.用transient关键字标记的成员变量不参与序列化过程

2.Parcelable接口

Parcelable接口是Android SDK提供的一种专门用于Android应用中对象的序列化和反序列化的方式,相比于Seriablizable具有更好的性能。实现Parcelable接口的对象就可以实现序列化并可以通过Intent和Binder传递。

下面是一个完成的实现了Parcelable接口的类

public class User implements Parcelable{
    public int userId;
    public String userName;
    public String password;
    public Book book;

    public User(int userId,String userName,String password,Book book){
        this.userId=userId;
        this.userName=userName;
        this.password=password;
        this.book=book;
    }

    public int describeContents(){
        //几乎所有情况都返回0,仅在当前对象中存在文件描述符时返回1
        return 0;
    }

    public void writeToParcel(Parcel out,int flags){
        out.writeInt(userId);
        out.writeString(userName);
        out.writeString(password);
        out.writeParcelable(book,0);
    }

    public static final Parcelable.Creator CREATOR=new Parcelable.Creator(){
        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();
        password=in.readString();       book=in.readParcelable(Thread.currentThread().getContextClassLoader());
    }
}

看起来比Serializable方式复杂太多。我们使用表格把Parcelable方式的相关方法进行说明

方法 功能 标记位
createFromParcel(Parcel in) 从序列化后的对象中创建原始对象
newArray(int size) 创建指定长度的原始对象数组
User(Parcel in) 从序列化后的对象中创建原始对象
writeToParcel(Parcel out,int flags) 将当前对象写入序列化结构中 PARCALABLE_WRITE_RETURN_VALUE
describeContents 返回当前对象的内容描述,几乎所有情况都返回0,仅在当前对象中存在文件描述符时返回1 CONTENTS_FILE_DESCRIPTOR

既然Parcelable和Serializable都可以实现序列化并且可以用于Intent间的数据传递,那么两者有什么区别呢?

区别 Serializable Parcelable
所属API JAVA API Android SDK API
原理 序列化和反序列化过程需要大量的I/O操作 序列化和反序列化过程不需要大量的I/O操作
开销 开销大 开销小
效率 很高
使用场景 序列化到本地或者通过网络传输 内存序列化

你可能感兴趣的:(Andoid)