在Android中,一般使用序列化時,谷歌官方都推薦咱們使用
Parcelable
來實現,由於效率比jdk提供的Serializable
要高不少(大約10倍)。java
這裏咱們首先先探討一下Parcelable
怎麼用,而後從源碼出發解讀Parcelable
的效率爲何這麼高。最後分析一下Parcelable
的應用場景,以及和Serializable
的區別。android
按照以下方式定義一個實現了Parcelable
的POJO對象,c++
import android.os.Parcel;
import android.os.Parcelable;
public class Book implements Parcelable {
private int id;
private String name;
//setter & getter & constructor
//...
//下面是實現Parcelable接口的內容
//除了要序列化特殊的文件描述符場景外,通常返回零就能夠了
@Override
public int describeContents() {
return 0;
}
//序列化
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(id);
dest.writeString(name);
}
public static final Creator<Book> CREATOR = new Creator<Book>() {
@Override
public Book createFromParcel(Parcel in) {
//自定義的私有構造函數,反序列化對應的成員變量值
return new Book(in);
}
@Override
public Book[] newArray(int size) {
return new Book[size];
}
};
//根據反序列化獲得的各個屬性,生成與以前對象內容相同的對象
private Book(Parcel in) {
//切記反序列化的屬性的順序必須和以前寫入的順序一致!!
id = in.readInt();
name = in.readString();
}
}
複製代碼
如下代碼展現瞭如何在Activity
之間經過序列化的方式傳遞Book
對象的數據,ide
//傳遞
Book book = new Book(123, "Hello world");
Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
intent.putExtra("book_data", book);
startActivity(intent);
//接受
Book book = (Book) getIntent.getParcelableExtra("book_data);
複製代碼
咱們進入源碼查看,Parcelable
只是一個接口,實現是交給Parcel
對象進行的。好比咱們在writeToParcel
時會調用Parcel
類中的方法,進入其中能夠看到實現是交給native作的,函數
...
public final void writeInt(int val) {
nativeWriteInt(mNativePtr, val);
}
...
@FastNative
private static native void nativeWriteInt(long nativePtr, int val);
@FastNative
private static native void nativeWriteLong(long nativePtr, long val);
@FastNative
private static native void nativeWriteFloat(long nativePtr, float val);
...
複製代碼
既然是native實現,就須要去看Android源碼了。咱們打開androidxref.com網站(可能須要fq,國內有個鏡像站推薦一下aospxref.com),在Android源碼中搜索nativeWriteInt
,定位到對應的c++實現位於目錄frameworks/base/core/jni/android_os_Parcel.cpp
,有興趣的朋友能夠瀏覽一下。網站
Android源碼裏不少native方法都是動態註冊的,這裏再也不贅述如何找到對應c的實現,咱們直接往下看,this
static void android_os_Parcel_writeInt(JNIEnv* env, jclass clazz, jlong nativePtr, jint val) {
//經過指針強轉拿到native層的Parcel對象
Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
if (parcel != NULL) {
//最終仍是調用的Parcel中的writeInt32函數
const status_t err = parcel->writeInt32(val);
if (err != NO_ERROR) {
signalExceptionForError(env, clazz, err);
}
}
}
...
//實際調用的是一個通用的模版方法
status_t Parcel::writeInt32(int32_t val)
{
return writeAligned(val);
}
...
//模版方法
//其中
//mData表示指向Parcel內存的首地址
//mDataPos表示指向Parcel空閒內存的首地址
//mDataCapacity表示Parcel分配內存的大小
template<class T> status_t Parcel::writeAligned(T val) {
COMPILE_TIME_ASSERT_FUNCTION_SCOPE(PAD_SIZE(sizeof(T)) == sizeof(T));
//先判斷加上val後會不會超過可用大小
if ((mDataPos+sizeof(val)) <= mDataCapacity) {
restart_write:
//reinterpret_cast是c++的再解釋強制轉換操做
//首先會計算mData + mDataPos獲得物理地址,轉成指向T類型的指針(T類型就是實際入參的類型)
//而後將val賦值給指針指向的內容
*reinterpret_cast<T*>(mData+mDataPos) = val;
//主要邏輯是修改mDataPos的偏移地址
//將偏移地址加上新增長的數據的字節數
return finishWrite(sizeof(val));
}
//若是超過了可用大小,執行增加函數
//以後再goto到上面的restart_write標籤執行寫入邏輯
status_t err = growData(sizeof(val));
if (err == NO_ERROR) goto restart_write;
return err;
}
複製代碼
經過上述代碼分析,writeInt32
函數會將數據寫入到一段共享的內存中,因此同理咱們在readInt
時,也是經過Parcel
對象從該段內存中讀取對應的值的。作同理分析,以下,spa
template<class T> status_t Parcel::readAligned(T *pArg) const {
COMPILE_TIME_ASSERT_FUNCTION_SCOPE(PAD_SIZE(sizeof(T)) == sizeof(T));
if ((mDataPos+sizeof(T)) <= mDataSize) {
//獲取讀取數據的地址
const void* data = mData+mDataPos;
//mDataPos指向下一個數據
mDataPos += sizeof(T);
//根據數據指針類型取出數據
*pArg = *reinterpret_cast<const T*>(data);
return NO_ERROR;
} else {
return NOT_ENOUGH_DATA;
}
}
複製代碼
寫數據時在一塊Parcel內存地址中,寫入12,34;讀取數據時從起始地址(Android系統在讀取時會將mDataPos重置爲起始值)+指針類型對應的字節數來一一讀取,首先讀取12,而後讀取34。指針
這也就是爲何咱們在寫序列化方法時,必定要將對應的成員變量的讀取/寫入順序保持一致的緣由。rest
Parcelable
在對與只須要進行內存序列化的操做時很快,由於Serializable
須要頻繁的進行I/O。Parcelable
實現較爲複雜且要注意讀寫順序的一致性,Serializable
相對來講實現很簡單。Parcelable
不適合用於作數據持久化,而Serializable適合。