Android 序列化 & 反序列化

What?

何為序列化與反序列化?
序列化:將對(duì)象轉(zhuǎn)化為二進(jìn)制序列的過(guò)程
反序列化:將二進(jìn)制序列恢復(fù)為原始對(duì)象的過(guò)程

Why?

為什么需要序列化?
由于在系統(tǒng)底層,數(shù)據(jù)以簡(jiǎn)單的字節(jié)序列形式進(jìn)行傳遞,即在底層,系統(tǒng)不認(rèn)識(shí)對(duì)象,只認(rèn)識(shí)字節(jié)序列,所以為了達(dá)到跨進(jìn)程通訊的目的,需要先對(duì)數(shù)據(jù)進(jìn)行序列化;其次,在進(jìn)行網(wǎng)絡(luò)數(shù)據(jù)傳輸或者activity間對(duì)象傳遞時(shí),也需要先將對(duì)象轉(zhuǎn)化為字節(jié)序列。

How?

如何進(jìn)行序列化?
在Android中,序列化操作有兩種方式:實(shí)現(xiàn)Serializabale接口或?qū)崿F(xiàn)Parcelable接口。
Serializabale接口
Serializabale接口是一個(gè)空接口,實(shí)際上只提供標(biāo)記的功能,標(biāo)記實(shí)現(xiàn)了該接口的對(duì)象是可以進(jìn)行序列化的,而具體的序列化與反序列化操作是由ObjectOutputStream和ObjectInputStream完成的。序列化與反序列化過(guò)程均對(duì)用戶透明,其中需要保存許多額外的字段以保證反序列化過(guò)程能夠順利完成,同時(shí),在這個(gè)過(guò)程中,還涉及到Java反射機(jī)制,所以整體時(shí)空開(kāi)銷(xiāo)比較大。
Parcelable接口
Parcelable是Android提供的接口,它主要是通過(guò)writeToParcel(),將需要持久化的字段保存到一個(gè)Parcel對(duì)象里面,然后通過(guò)CREATOR從Parcel對(duì)象中,取出相應(yīng)的字段,完成對(duì)象的恢復(fù)過(guò)程。這整個(gè)過(guò)程均由用戶自己控制,可以自定義保存和恢復(fù)的字段,所以存儲(chǔ)代價(jià)小很多。
Serializabale VS Parcelable
實(shí)際開(kāi)發(fā)中,推薦使用Parcelable接口,理由大致有如下三點(diǎn):首先,Parcelable接口是Android提供使用的,Google提供了比較好的文檔和技術(shù)支持;其次,Parcelable接口底層是內(nèi)存的copy,而Serializable底層是文件IO操作,同時(shí)會(huì)使用到反射技術(shù),所以效率上,Parcelable要遠(yuǎn)高于Serializable;最后,Parcelable具有更好地可控性,我們可以自己控制需要保存和恢復(fù)的字段,同時(shí)節(jié)省空間開(kāi)銷(xiāo)。Parcleable主要用于內(nèi)存序列化,通過(guò)Parcelable將對(duì)象序列化到存儲(chǔ)設(shè)備中或者將對(duì)象序列化后通過(guò)網(wǎng)絡(luò)進(jìn)行傳輸也是可以的,但是這個(gè)過(guò)程會(huì)稍顯復(fù)雜,因此在這兩種情況下,建議使用Serializable。

序列化相關(guān)知識(shí)

  1. 序列化 ID
    當(dāng)通過(guò)實(shí)現(xiàn)Serializable接口實(shí)現(xiàn)類的序列化操作時(shí),需要提供一個(gè)序列化ID,即聲明private static final long serialVersionUID,原則上序列化后的數(shù)據(jù)中的serialVersionUID只有和當(dāng)前類的serialVersionUID相同時(shí),才能夠進(jìn)行正常的反序列化操作,否則反序列化過(guò)程會(huì)失敗。serialVersionUID的詳細(xì)工作機(jī)制為:序列化的時(shí)候,系統(tǒng)會(huì)將當(dāng)前類的serialVersionUID寫(xiě)入序列化文件/指定文件中,當(dāng)反序列化的時(shí)候,系統(tǒng)會(huì)檢測(cè)文件中的serialVersionUID與當(dāng)前類中的serialVersionUID是否一致,若一致,說(shuō)明序列化的類的版本與當(dāng)前類的版本相同,這個(gè)時(shí)候可以成功進(jìn)行反序列化操作;否則,說(shuō)明當(dāng)前類和序列化的類相比,發(fā)生了某些變化,如成員變量的數(shù)量、類型等發(fā)生變化,這時(shí)反序列化操作就無(wú)法正常完成。

  2. 靜態(tài)變量序列化
    靜態(tài)變量是類變量,不屬于某個(gè)對(duì)象,而序列化操作保存的是對(duì)象的狀態(tài),即對(duì)象的成員變量,所以靜態(tài)變量不會(huì)參與序列化過(guò)程。

  3. 父類的序列化
    如果希望一個(gè)類的父類也被序列化,則該父類也應(yīng)實(shí)現(xiàn)序列化接口。

  4. Transient 關(guān)鍵字
    Transient用于聲明一個(gè)變量不參與序列化過(guò)程,所以當(dāng)希望某些變量不被序列化時(shí),就可以使用該關(guān)鍵字修飾這些變量。被Transient關(guān)鍵字聲明的變量,在反序列化時(shí),會(huì)被設(shè)置為初始值,即int類型變量為0,對(duì)象類型變量為null。

  5. 對(duì)敏感字段加密
    在消息傳輸?shù)倪^(guò)程中,有些字段是敏感字段,不希望被泄露,如用戶密碼等,在這種情況下,進(jìn)行序列化操作時(shí),應(yīng)先對(duì)敏感字段進(jìn)行加密操作,反序列化時(shí)再進(jìn)行解密。具體解決辦法為:重寫(xiě)類中的writeObject和readObject方法。因?yàn)樵谛蛄谢^(guò)程中,虛擬機(jī)會(huì)先調(diào)用待序列化類中的writeObject和readObject方法,若該方法存在,則使用該方法完成用戶自定義的序列化和反序列化操作,否則,調(diào)用 ObjectOutputStream 的 defaultWriteObject 方法以及 ObjectInputStream 的 defaultReadObject 方法進(jìn)行默認(rèn)的序列化和反序列化操作。

  6. 序列化存儲(chǔ)規(guī)則
    Java 序列化機(jī)制為了節(jié)省磁盤(pán)空間,具有特定的存儲(chǔ)規(guī)則,當(dāng)對(duì)同一對(duì)象進(jìn)行多次序列化操作時(shí),并不會(huì)對(duì)該對(duì)象的內(nèi)容進(jìn)行多次存儲(chǔ),而只存儲(chǔ)多份引用,這樣就只需要保存新增的引用及一些控制信息。

ArrayList的序列化

ArrayList的定義
ArrayList保存內(nèi)容的數(shù)組

由上圖一可知,ArrayList實(shí)現(xiàn)了Serializable接口,可以進(jìn)行序列化操作,而由圖二可知,其中保存內(nèi)容的數(shù)組array被transient關(guān)鍵字修飾,不會(huì)被序列化,那么問(wèn)題來(lái)了,這到底是咋回事呢?
原因如下圖三所示:

圖三

ArrayList內(nèi)部實(shí)現(xiàn)了writeObject和readObject方法,通過(guò)這兩個(gè)方法自己控制序列化過(guò)程。
Why transient?
ArrayList為什么使用transient使array不被序列化,然后又自定義序列化過(guò)程呢?
因?yàn)锳rrayList是一個(gè)動(dòng)態(tài)數(shù)組,經(jīng)常會(huì)成倍自增長(zhǎng)長(zhǎng)度,當(dāng)數(shù)組中實(shí)際存放的元素很少,而申請(qǐng)的長(zhǎng)度比較大時(shí),直接進(jìn)行序列化,就會(huì)生成很多null元素,所以為了避免不必要的null元素的生成,及提高時(shí)空效率,ArrayList將array用transient關(guān)鍵字進(jìn)行聲明,然后再自己控制元素的序列化過(guò)程。

總結(jié)

  1. 在進(jìn)行跨進(jìn)程通信、activity間數(shù)據(jù)傳輸以及網(wǎng)絡(luò)數(shù)據(jù)傳輸時(shí),需要將原始的對(duì)象類型轉(zhuǎn)化為字節(jié)序列,這是就需要使用到對(duì)象的序列化與反序列化操作;
  2. 序列化即將對(duì)象或數(shù)據(jù)結(jié)構(gòu)轉(zhuǎn)化為字節(jié)序列的過(guò)程;反序列化即將字節(jié)序列恢復(fù)為原始對(duì)象的過(guò)程;序列化保存的是對(duì)象的“狀態(tài)”,即對(duì)象的成員變量,所以靜態(tài)變量、靜態(tài)方法等屬于類的屬性以及被transient關(guān)鍵字聲明的不用于序列化的屬性,均不會(huì)被序列化;
  3. Android中序列化操作有兩種方式:實(shí)現(xiàn)Serializable接口和實(shí)現(xiàn)Parcleable接口。Serialiable是Java提供的序列化接口,使用起來(lái)比較簡(jiǎn)單,但是開(kāi)銷(xiāo)很大,序列化和反序列化均需要大量I/O操作,而Parcelable是Android提供的序列化方式,因此更適合用于Android平臺(tái),所以在Android平臺(tái)上,首選Parcelable接口。
  4. ArrayList的序列化過(guò)程比較特殊,需要注意。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容