本文先講下Parcelable和Serializable,下篇文章在將Binder,然后再說多進程的處理方式,只有了解了Parcelable和Serializable還有Binder,才能更好的理解快進程通信的各種方式。
Parcelable和Serializable接口可以完成對象的序列化過程,當(dāng)我們需要通過Intent和Binder傳輸數(shù)據(jù)的時候需要使用到Parcelable和Serializable。還有我們需要吧對象持久化的存儲到設(shè)備上或者通過網(wǎng)絡(luò)傳輸給其他客戶端的時候,這個時候也要用到Serializable來完成對象的持久化。下邊先來講解下Serializable是怎樣來完成對象的序列化的。
1.Serializable接口
(1) 怎樣使用Serializable來實現(xiàn)序列化
使用Serializable來完成序列化其實非常加單,只需要在類的聲明中指定一個累死下面的標(biāo)識即可自動實現(xiàn)默認的序列化過程,當(dāng)然前提是你需要實現(xiàn)Serializable。
private static final long serialVersionUID = 7247714666080613254L;
什么,這么長你記不住,沒關(guān)系,7247714666080613254L這個就是我瞎寫的,當(dāng)然你不能這么做,在Android Studio中你可以這樣做,在Eclipse里邊你可以這樣做(我沒有嘗試Eclipse,所以不知道正確與否,試過的請留言是否可以,謝謝!!)
好了,下邊來講解下怎么實現(xiàn)序列化和反序列化以及為什么這么寫就可以序列化
(2) 怎么實現(xiàn)序列化和反序列化
PS:此代碼就是采用了Serializable方式序列化對象的典型過程,很簡單,只需要吧實現(xiàn)了Serializable接口的bean類的對象寫到文件中就可以快速恢復(fù)了,恢復(fù)后的對象和寫進去的數(shù)據(jù)是一樣的,但是兩者不是同一個對象。
或許看到這里很多人會有一種疑問,serialVersionUID如果不指定可以嗎?如果指定,那么他后邊那一長串?dāng)?shù)字又是什么意思呢?下邊我們來分析下。
(3) 為什么這樣做
我們要明白,系統(tǒng)既然提供了這個serialVersionUID,那么它必定是有用的。serialVersionUID是用來輔助序列化和反序列化過程的,原則上序列惡化后的數(shù)據(jù)的serialVersionUID只有和當(dāng)前類的serialVersionUID相同才能夠正常的被反序列化,serialVersionUID的詳細工作機制是這樣的:序列化的時候系統(tǒng)會把當(dāng)前類的serialVersionUID寫入序列化的文件中(也可能是其他的中介),當(dāng)反序列化的時候系統(tǒng)會去檢測文件中的serialVersionUID,看它是否和當(dāng)前類的serialVersionUID一致,如果一致就說明序列化的類的版本和當(dāng)前類的版本是相同的,這個時候就可以成功反序列化;否則就說明當(dāng)前類和序列化的類相比發(fā)生了某些變換,比如成員變量的數(shù)量、類型可能發(fā)生了變換,這個時候是無法正常反序列化的。
一般來說,我們應(yīng)該手動指定serialVersionUID的值,比如1L,也可以讓eclipse根據(jù)當(dāng)前類的結(jié)構(gòu)自動生成他的hash值,這樣序列化和反序列化的時候兩者的serialVersionUID是相同的,因此可以正常進行反序列化。如果不手動指定serialVersionUID的值,反序列化的時候當(dāng)前類有所改變,比如增加刪除了某些成員變量,那么系統(tǒng)會重新計算當(dāng)前類的hash值并把它賦值給serialVersionUID,這個時候當(dāng)前類的serialVersionUID就和序列化的數(shù)據(jù)中的serialVersionUID不一致,于是就會反序列化失敗。當(dāng)然我們還要考慮另外一種情況,如果類結(jié)構(gòu)發(fā)生了非常規(guī)性的改變,比如修改了類名,修改了成員變量的類型,這個時候盡管serialVersionUID驗證通過了,但是反序列化過程還是會失敗,因為類結(jié)構(gòu)發(fā)生了毀滅性的改變,根本無法從老版本的數(shù)據(jù)中還原出一個新的類結(jié)構(gòu)的對象。
根據(jù)上面的分析,我們可以知道,給serialVersionUID指定為1L或者采用eclipse根據(jù)當(dāng)前類結(jié)構(gòu)去生成的hash值,這兩者沒有本質(zhì)的區(qū)別,效果完全一樣。以下兩點需要特別提一下,首先靜態(tài)成員變量屬于類不屬于對象,所以不參與序列化的過程;其次用transient關(guān)鍵字標(biāo)記過的成員變量不參與序列化操作。
2. Parcelable接口
相對于Serializable來說,Parcelable操作起來就沒有那么麻煩了,畢竟有線程的插件可以使用,直接生成就好啦。來看一個例子
大碼如下
public class StaticPubliccTools implements Parcelable {
public static int userId = 1;
private String name;
private boolean sex;
private int age;
private Book book;
/**
* 返回當(dāng)前對象的內(nèi)容描述 如果含有文件描述符 返回1 否則返回0 幾乎所有情況都返回0
* @return
*
* 標(biāo)記位 CONTENTS_FILE_DESCRIPTOR
*/
@Override
public int describeContents() {
return 0;
}
/**
* 將當(dāng)前對象寫入序列化的機構(gòu)中,其中flags表示有兩種值 0 或者 1 為4時標(biāo)識當(dāng)前對象需要作為返回值返回
* 不能立即釋放資源 幾乎所有的情況都為0
* @param dest
* @param flags
*
* 標(biāo)記位 PARCELABLE_WRITE_RETURN_VALUE
*/
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(this.name);
dest.writeByte(this.sex ? (byte) 1 : (byte) 0);
dest.writeInt(this.age);
dest.writeParcelable(this.book, flags);
}
public StaticPubliccTools() {
}
/**
* 從序列化后的對象中創(chuàng)建原始對象
* @param in
*/
protected StaticPubliccTools(Parcel in) {
this.name = in.readString();
this.sex = in.readByte() != 0;
this.age = in.readInt();
this.book = in.readParcelable(Book.class.getClassLoader());
}
public static final Parcelable.Creator<StaticPubliccTools> CREATOR = new Parcelable.Creator<StaticPubliccTools>() {
/**
* 從序列化后的對象中創(chuàng)建原始對象
* @param source
* @return
*/
@Override
public StaticPubliccTools createFromParcel(Parcel source) {
return new StaticPubliccTools(source);
}
/**
* 創(chuàng)建指定長度的原始對象數(shù)組
* @param size
* @return
*/
@Override
public StaticPubliccTools[] newArray(int size) {
return new StaticPubliccTools[size];
}
};
}
這里先說一下Parcel,Parcel內(nèi)部包裝了可序列化的數(shù)據(jù),可以再Binder中自由傳輸。從上述代碼中我們可以看出,在序列化的過程中需要實現(xiàn)的功能有序列化,反序列化和內(nèi)容描述,序列化功能由writeToParcel方法來完成,最終通過Parcel中的一些列write方法來完成的,反序列化功能有CREATOR來完成,內(nèi)部標(biāo)明了如何創(chuàng)建序列化對象和數(shù)組,并通過Parcel的一系列read方法來完成反序列化過程;內(nèi)容描述功能有describeContents方法來完成,幾乎在所有情況下這個方法都返回0,僅當(dāng)當(dāng)前對象中存在文件描述時,此方法才返回1,需要注意的是,在StaticPubliccTools(Parcel in)方法中,由于book是另一個可序列化對象,所以他的反序列化過程需要傳遞當(dāng)前線程的上下文類加載器,否則會報無法找到類的錯誤
3. 總結(jié)
系統(tǒng)已經(jīng)為我們提供了許多實現(xiàn)了Parcelable的接口的類,他們都是可以直接序列化的。比如說Intent、Bundle、Bitmap等,同時List和Map也是可以序列化的,前提是他們里邊的每個元素都是可序列化的。
既然Parcelable和Serializable都是實現(xiàn)序列化并且用于Intent間的數(shù)據(jù)傳遞,那么二者該如何選擇呢?Serializable是Java中的序列化接口,使用起來簡單但是開銷大,序列化和反序列化的過程都需要大量的I/O操作,而Parcelable是Android中的序列化方式,因此更適合用在Android平臺上。他的缺點是使用起來稍微麻煩點,不過上邊已經(jīng)說了,咱們有插件啊,他的效率也非常高。這是Android推薦的序列化方式。
Parcelable主要用在內(nèi)存序列化上,通過Parcelable將對象序列化到存儲設(shè)備中或者將對象序列化后通過網(wǎng)絡(luò)傳輸也都是可以的,但是這個過程非常復(fù)雜,淫才這兩種情況下建議大家使用Serializable。