Parcelable vs Serializable 性能對比
Android Parcel對象詳解
Parcelable最強解析
[toc]
當我們使用Intent傳遞一個對象的時候,需要實現序列化接口或者實現Parcelable接口。
用法很容易找到資料,這里不再贅述,推薦看這篇文章:序列化Serializable和Parcelable的理解和區別
下面主要分析下這兩者間的原理和性能對比。
1. Serializable原理
來看一個很簡單的類(直接從其它文章中復制過來的):
class TestSerial implements Serializable {
public byte version = 100;
}
序列化之后是一個字節序列。轉化成十六進制解釋的話如下:
AC ED (序列化協議)
00 05 (序列化版本)
73 (TC_OBJECT. 新的對象)
72 (TC_CLASSDESC. 這是一個新類描述)
00 0A (類名的長度)
53 65 72 69 61 6C 54 65 73 74 (類的名稱)
05 52 81 5A AC 66 02 F6 (SerialVersionUID)
02 (Various flags,0x02代表這個對象支持序列化)
00 01 (類有幾個字段)
49 (代表是int類型)
00 07 (字段名稱的長度)
76 65 72 73 69 6F 6E (version, 字段的名稱)
78 (TC_ENDBLOCKDATA, 描述的結束符)
70 (TC_NULL)
00 00 00 64 (version的值)
將一個對象序列化的時候,會首先寫入一些額外的信息,例如序列化協議、版本。然后是關于該對象的一些描述,例如類名和它的長度。最后才是對象中屬性。然而version = 100這個簡單的屬性用了非常復雜的方式保存,包含字段名、字段名長度、字段類型、字段值,所有的這些都是通過反射的方式獲取的。
想象一下如過類中有一個方法,那這個方法的序列化估計也是很復雜的,例如方法的返回值類型,入參類型,入參個數等。
所以序列化一個對象的開銷還是比較大的。更何況還有相同復雜程度的反序列化過程。然而這個過程又是必須的,因為序列化后的對象可能會交給另一個程序使用,這個對象的信息需要完整的保存下來。
2. Parcelable原理
對比序列化,Parcelable(這個不叫序列化,有人老喜歡將這個稱為序列化,不懂英文嗎)則輕量級很多,因為它們的實現目的不一樣。
序列化是為了持久化一個對象,可以保存在本地,也可以網絡傳輸,需要保證這個對象的完整性。而Parcelable的目的只是打包一組數據在Android應用組建之間傳輸,所以只需要在內存中保存即可,所以它不需要用到反射獲取屬性字段,也不需要保存額外的header信息,更不需要保存方法字段。
來看看一個簡單的實現了Parcelable接口的對象:
public class Test implements Parcelable {
int i = 10;
double d = 1.23456d;
public static final Creator<Test> CREATOR = new Creator<Test>() {
@Override
public Test createFromParcel(Parcel in) {
return new Test(in);
}
@Override
public Test[] newArray(int size) {
return new Test[size];
}
};
@Override
public int describeContents() {
return 0;
}
protected Test(Parcel in) {
i = in.readInt();
d = in.readDouble();
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(i);
dest.writeDouble(d);
}
}
很明顯可以看到CREATOR是通過Test(Parcel in)
這個構造來恢復一個對象的,而Parcel保存著這個對象的一些信息。這些信息由writeToParcel
方法寫入到Parcel中,傳輸后再由Test(Parcel in)
恢復。
由此可以看出Parcelable和Serilizable很大的區別,Parcelable只是將對象中的數據打包起來存入內存,不需要記錄它字段名類名等,對比序列化真的是簡單太多了。
Parcel是通過調用c/c++將數據直接存到內存中,具體實現這里就不分析了,看參考文章中有大致解釋。這里簡單做簡單說明:
Parcel是通過一段內存空間來保存數據的,當writeInt(i)
調用時,往內存中寫入一段32位的數據,而當writeDouble(d)
調用時,在后面又追加一段長度為64位的數據。而讀取的時候,也是按順序讀取,readInt()
會讀取前32位的數據,轉換成int類型,讀取接著的64位,轉換成double。這就是為什么Parcelable的write方法和read方法順序要一致的原因。當然,實際存儲情況會更復雜,這里就不探究了,有興趣的自行查資料。
3. 總結
Serializable會序列化對象到一個字節序列中,這個字節序列保存了一些必要的header信息,還保存了類的描述,類的方法,對象的數據等,而這些信息都是通過反射的方式獲取的,不僅更消耗內存,還更加消耗性能。
而Parcelable則是打包對象中的一些數據,將它們的值按順序拼接起來,保存到內存中,讀取的時候也按順序讀取它們的長度。無需保存字段名,類名等額外信息,也用不上反射。所以Parcel對比序列化是更節省內存和更加高效的。
有人(看頂部引用的文章)對比過兩者的性能差距有十倍左右,但也只是毫秒級別的,所以如果是簡單的對象,使用Parcelable或Serialable,從人類的角度來看是沒有區別的。
所以,選擇Parcelable或是Serializable應該由程序員自己判定,前者更節省內存,更高效,但是使用起來麻煩,增加維護成本。而后者使用非常簡單,但是更耗費內存和性能。
另外再說下,切勿使用Serializable或者Parcelable在組建中傳遞高內存消耗的對象,例如大圖Bitmap,可能會導致內存溢出。
例如ActivityA中有一張圖片,序列化或者打包(Parcel)時,內存中又會保存這個圖片,而到達ActivityB時,又會重新實例化著圖片,一共使用類三份內存。而直接將圖片保存到本地,在ActivityB中重新加載,只消耗了兩份內存。