Android中的序列化方式

前言

在实际开发过程中,我们常常会有序列化对象的需求。在Android中,可以使用两种方式实现对象的序列化,即使用SerializableParcelable接口。

Serializable方式

Serializable接口是Java提供的序列化接口。实际上,这是一个空接口,只是用来提供标记作用。真正的序列化和反序列化过程全部由系统实现,因此使用起来会觉得很方便。下面就给出一个简单的例子演示Serializable接口的使用:

实现Serializable接口的类:

public class Reader implements Serializable {
    private static final long serialVersionUID = 1L;

    private int readerId;
    private String name;

    public Reader(int readerId, String name) {
        this.readerId = readerId;
        this.name = name;
    }

    @Override
    public String toString() {
        return "readerId:"+readerId+" name:"+name;
    }

    public int getReaderId() {
        return readerId;
    }
    public void setReaderId(int readerId) {
        this.readerId = readerId;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

可以看到,在Reader类中有这样一段代码:

private static final long serialVersionUID = 1L;

这段代码作用是为序列化后的对象写入一个serialVersionUID值。这个long值可以标识对象所属的类结构是否发生变化。在进行反序列化的时候,只有序列化对象的serialVersionUID值和Reader类的serialVersionUID值一致才能够执行成功。一般情况下,我们将这个值指定为1L即可。

序列化过程:

//序列化
ObjectOutputStream objectOutStream=null;
try {
    Reader reader=new Reader(1,"Tom");
    objectOutStream=new ObjectOutputStream(new FileOutputStream("reader.txt"));
    objectOutStream.writeObject(reader);
} catch (IOException e) {
    e.printStackTrace();
}finally{
    //关闭资源
    if(objectOutStream!=null){
        try {
            objectOutStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

关键代码是这四句:

ObjectOutputStream objectOutStream=null;
Reader reader=new Reader(1,"Tom");
objectOutStream=new ObjectOutputStream(new FileOutputStream("reader.txt"));
objectOutStream.writeObject(reader);

可以看到,通过ObjectOutputStream对象的writeObject方法,我们可以很方便地将Reader对象写入reader.txt这个文件中。实际上,这个文件名可以随意定义,即使没有后缀名也可以。只要在反序列化时使用同样的文件名即可。

反序列化过程:

//反序列化
ObjectInputStream objectInStream=null;
try {
    objectInStream=new ObjectInputStream(new FileInputStream("reader.txt"));
    try {
        Reader reader=(Reader) objectInStream.readObject();
        System.out.println(reader);
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
} catch (IOException e) {
    e.printStackTrace();
}finally{
    //关闭资源
    if(objectInStream!=null){
        try {
            objectInStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

关键代码是这三句:

ObjectInputStream objectInStream=null;
objectInStream=new ObjectInputStream(new FileInputStream("reader.txt"));
Reader reader=(Reader) objectInStream.readObject();

可以看到,通过ObjectInputStream对象的readObject方法,我们可以从reader.txt这个文件中恢复Reader对象的内容(需要进行强制转型)。需要注意的是,通过这些步骤,我们只能恢复原来那个Reader对象的内容。但是从本质上来看,通过这种方式获得的Reader对象和原来的Reader对象并不是同一个东西。

在一个实现了Serializable接口的类中,成员变量除了可以包含基本类型,也可以包含其它实现了Serializable接口的对象。系统在进行序列化的时候会依次执行各个对象的序列化过程。

还需要知道的是,类的静态变量并不会被序列化。因为静态变量属于类,并不属于某一个对象。此外,transient关键字修饰的变量不会被序列化

Parcelable方式

出于对序列化效率的需求,Android也提供了自己独有的序列化接口Parcelable。这个接口的使用方式相比Serializable复杂许多,但是却可以获得更高的性能。下面同样给出一个简单的例子:

实现Parcelable的类:

public class Book implements Parcelable {
    private int bookId;
    private String name;

    public Book(int bookId,String name) {
        this.bookId=bookId;
        this.name = name;
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(bookId);
        dest.writeString(name);
    }

    public static final Parcelable.Creator<Book> CREATOR=new 
            Parcelable.Creator<Book>(){
        @Override
        public Book createFromParcel(Parcel source) {
            return new Book(source);
        }

        @Override
        public Book[] newArray(int size) {
            return new Book[size];
        }
    };

    //用于反序列化Book的私有构造方法
    private Book(Parcel source){
        bookId=source.readInt();
        name=source.readString();
    }
    //getter、setter方法
    ........
}

Book类实现Parcelable接口,需要实现writeToParceldescribeContents这两个方法。writeToParcel用于对数据进行序列化,通过Parcel对象的一系列write方法,逐步将成员变量序列化。describeContent方法通常返回0即可,这是一个描述信息。

而Book的反序列化过程则由静态常量Parcel.Creator实现。我们通过匿名内部类的方式实例化了一个Parcel.Creator对象,并重写了createFromParcelnewArray这两个方法。在newArray方法中,我们简单地返回一个Book数组即可,即return new Book[size]createFromParcel方法则真正实现了对象的反序列化过程。我们从Parcel逐次读取成员变量的内容,并通过私有的构造方法实例化一个Book对象。这个对象的内容和被序列化的对象内容一致。当然了,和Serializable相同,这样获得的Book对象和原来的对象并不是同一个东西。需要注意的是,读取数据的顺序必须和写入数据的顺序相同,否则反序列化一定会失败。其次,Parcel.Creator对象只能用publicprotected修饰,否则反序列化同样会失败。

以上只是一个简单的例子,Book类的成员变量都是基本数据类型。但是很多时候,我们的类中还会包含其它对象。这就涉及到如何序列化一个Parcelable成员以及如何反序列化一个Parcelable成员,以下将进行简单讲解。

一个相对复杂的Parcelable示例:

public class Reader implements Parcelable {
    private int readerId;
    private String name;

    private Book book;

    public Reader(int readerId, String name, Book book) {
        this.readerId = readerId;
        this.name = name;
        this.book = book;
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(readerId);
        dest.writeString(name);
        dest.writeParcelable(book,0);
    }

    public static final Parcelable.Creator<Reader> CREATOR=new
            Parcelable.Creator<Reader>(){
        @Override
        public Reader createFromParcel(Parcel source) {
            return new Reader(source);
        }

        @Override
        public Reader[] newArray(int size) {
            return new Reader[size];
        }
    };

    //用于反序列化Reader的私有构造方法
    private Reader(Parcel source){
        readerId=source.readInt();
        name=source.readString();
        //传入当前线程的上下文类加载器
        book=source.readParcelable(Thread.currentThread().getContextClassLoader());
    }
    //getter、setter方法
    .........
}

可以看到,Reader类中拥有一个Book成员,而Book已经实现了Parcelable接口。在writeToParcel方法中,我们使用了以下语句来序列化Book成员:

dest.writeParcelable(book,0);

第二个参数一般传入0即可。在反序列化过程中,我们使用了以下语句:

//传入当前线程的上下文类加载器
book=source.readParcelable(Thread.currentThread().getContextClassLoader());

我们在读取Book对象的时候,传入了一个ClassLoader,这是当前线程的上下文类加载器。如果不传入这个参数,系统将无法找到Book对应的类,这也就会导致反序列化失败。

两种方式的比较

通过上文的例子,我们可以明显感觉到,使用Serializable这种方式是相当简单的,具体细节已经全部被系统承包了。但是方便是需要付出代价的。使用Serializable的开销是很大的,因为需要进行频繁的IO操作,这也是Android推出Parcelable的原因。虽然Parcelable使用起来更加繁琐,但是却能获得更好的性能。

因此,在涉及内存序列化的场景下,如通过Intent传递序列化对象,建议使用Parcelable。而在涉及将对象序列化到存储设备中、或者将序列化的对象通过网络传输等场景下,建议使用Serializable

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章