android开发-android序列化

来源:互联网 发布:淘宝卖家修改发货地址 编辑:程序博客网 时间:2024/06/04 01:20

android序列化有Serializable和Parcelable方式

当我们需要通过Intent和Binder传输数据的时候就需要使用序列化,有的时候我们还需要把对象持久化到存储设备上或者通过网络传输到其他的客户端上

一、Serializable

Serializable是Java提供的一个序列化接口,他是一个空的接口,为对象提供了标准的序列化和反序列化操作,只要需要这个类实现了Serializable接口并声明一个serialVersionUID即可。甚至整个SerializableUID也不是必须的,也可以实现序列化功能,但是这对于反序列化会有影响。

public class User implements Serializable{、

private static final long serialVersionUID=.....;

public Sting name;

public int age;

public boolean isMale;

......

}

Serializable序列化实现起来非常简单,几乎所有的工作都被系统自动完成了,只需要采用ObjectOutputStream和ObjectInputStream即可实现

序列化的过程

User user=new User(0,"jack",true);

ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream("cache.txt"));

out.writeObject(user);

out.close(0;

反序列化的过程

ObjectInputStream in=new ObjectInputStream(new FileInputStream("cache.txt"));

User user=(User)in.readObject(in);

in.close();

我们写入的对象和恢复出来的对象 的内容是完全一样的  但是他们并不是同一个对象

serialVersionUID既然可用可不用,到底用不用呢,既然系统已经提供了这个东西,那么准定是有用的,这个东西是用来辅助序列化与反序列化过程的,原则上 序列化后的数据中的serialVersionUID 只有和当前类的serialVersionUID相同才能正常的反序列化

serialVersionUID的工作原理是这样的:序列化的时候 系统会把当前类的serialVersionUID写入序列化的文件中(或者其他的中介)当反序列化的时候系统回去检测文件中的

serialVersionUID,看他是否与当前的类的serialVersionUID保持一致,如果一致就说明序列化的类的版本号与当前类的版本是相同的,这个时候可以反序列化成功,

否则的话说明当前类和序列化的类相比发生了某种变换。比如增加了一个成员变量,或者变量的类型发生改变 这时候就无法反序列化成功了

一般来说 我们需要手动指定serialVersionUID的值,也可以让Eclipse根据当前类的结构自动生成他的hash值,这样序列化与反序列化时候两者的serialVersionUID是相同的

因此可以正常进行反序列化。

如果不手动指定serialVersionUID的值,反序列化的时候时候当前类有所改变的时候,那么系统会重新计算当前类的hash值并且把它赋值给serialVersionUID,那么他们的serialVersionUID就不一样 所以会导致反序列化失败,所以我们手动指定了它之后,就可以在很大程度上避免反序列化的失败,比如党版本升级之后,我们可能删除了某个成员变量或者增加了一个变量,这时候我们反序列化的时候也能成功。

如果类的结构发生了非常规的改变,比如类型修改了,或者成员变量的类型改变了,这个时候尽管serialVersionUID验证通过了,但是还是会crash

静态成员变量属于类不属于对象,所以不会参与序列化的过程

用transient关键字编辑的成员变量不会参与序列化的过程

二、实现Parcelable接口

Parcelable是android的序列化,一个类只要实现了Parcelable接口,他的对象就可以实现序列化 然后通过Intent以及Binder传递

Parcel内部包装了可序列化的数据。

public class User implements Parcelable {
    public String userName;
    public int userId;
    public boolean  isMale;
    public Book book;
    
    public User(String userName,int userId,boolean isMale) {
        this.userName=userName;
        this.userId=userId;
        this.isMale=isMale;
    }
    @Override
    public int describeContents() {
        return 0;
    }
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(this.userName);
        dest.writeInt(this.userId);
        dest.writeByte(this.isMale ? (byte) 1 : (byte) 0);
        dest.writeParcelable(this.book, flags);
    }
    protected User(Parcel in) {
        this.userName = in.readString();
        this.userId = in.readInt();
        this.isMale = in.readByte() != 0;
        this.book = in.readParcelable(Thread.currentThread().getContextClassLoader());
    }
    public static final Creator<User> CREATOR = new Creator<User>() {
        @Override
        public User createFromParcel(Parcel source) {
            return new User(source);
        }
        @Override
        public User[] newArray(int size) {
            return new User[size];
        }
    };
}

从代码中可以看到序列化的过程中需要实现:序列化、反序列化、内容描述

序列化功能由writeToParcelable方法来实现,最终通过Parcel中的一系列write方法来完成;

反序列化功能由CREATOR来完成,其内部标明了 如何创建序列化的对象和数组 并通过Parcel的一系列read方法来完成反序列化过程

内容描述功能由describeContents方法来完成,几乎在所有的情况下 这个方法都应该返回0,仅仅当前对象中存在文件描述符的时候返回1

需要注意的是 User(Parcel in) 方法中,由于book是另一个可序列化的对象,所以他的反序列化过程需要传递当前线程的上下文类加载器

this.book = in.readParcelable(Thread.currentThread().getContextClassLoader());

否则会报无法找到类的错误



Parcelable和Serializable都可用于Intent序列化,都可以用于Intent数据传递 那么二者如何选取呢

Serializable实现起来比较简单 但是开销很大,在序列化和反序列化过程中有大量的IO操作

Parcelable是android中的序列化方式,效率很高 但是写起来比较麻烦 但是在AS中 有Parcelable的插件 安装之后可以快速的帮我们书写完