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

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