前言
在實際開發過程中,我們常常會有序列化對象的需求。在Android中,可以使用兩種方式實現對象的序列化,即使用Serializable
和Parcelable
接口。
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接口,需要實現writeToParcel
和describeContents
這兩個方法。writeToParcel
用於對數據進行序列化,通過Parcel
對象的一系列write
方法,逐步將成員變量序列化。describeContent
方法通常返回0
即可,這是一個描述信息。
而Book的反序列化過程則由靜態常量Parcel.Creator
實現。我們通過匿名內部類的方式實例化了一個Parcel.Creator
對象,並重寫了createFromParcel
和newArray
這兩個方法。在newArray
方法中,我們簡單地返回一個Book數組即可,即return new Book[size]
。createFromParcel
方法則真正實現了對象的反序列化過程。我們從Parcel
逐次讀取成員變量的內容,並通過私有的構造方法實例化一個Book對象。這個對象的內容和被序列化的對象內容一致。當然了,和Serializable
相同,這樣獲得的Book對象和原來的對象並不是同一個東西。需要注意的是,讀取數據的順序必須和寫入數據的順序相同,否則反序列化一定會失敗。其次,Parcel.Creator
對象只能用public
或protected
修飾,否則反序列化同樣會失敗。
以上只是一個簡單的例子,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
。