Android Serializable與Parcelable原理與區別

一、序列化、反序列化是什麼?

(1) 名詞解釋

對象的序列化 : 把Java對象轉換爲字節序列並存儲至一個儲存媒介的過程。
對象的反序列化:把字節序列恢復爲Java對象的過程。

(2) 序列化詳細解釋

對象的序列化涉及三個點關鍵點:Java對象、字節序列、存儲。

1. Java對象的組成?
Java對象包含變量與方法。但是序列與反序列化僅處理Java變量而不處理方法,序列與反序列化僅對數據進行處理。

2. 什麼是字符序列?
字符序列是兩個詞,字符是在計算機和電信領域中,字符(Character)是一個信息單位。數學上,序列是被排成一列的對象(或事件)。
《字符-維基百科》 , 《序列-維基百科》 說白了就是連續排列的多個字符的集合。類似於1A165613246546

3. 存儲
字符序列需要保存到一個地方,可以是硬盤也可以是內存。
簡單說法是:序列化把當前對象信息保存下來。反序列化剛好相反的操作。


二、Java對象與Java對象序列化的區別?

Java對象存在的前提必須在JVM運行期間存在,如果想在JVM非運行的情況下或者在其他機器JVM上獲取指定Java對象,在現有Java對象的機制下都不可能完成。
與Java對象不同的是,如果對Java對象執行序列化操作,因爲原理是把Java對象信息保存到存儲媒介,所以可以在以上Java對象不可能存在的兩種情況下依然可以使用Java對象。


三、爲什麼要使用序列化、反序列化?

根據以上對序列化、反序列化的理解,這個疑問可以翻譯成,爲什麼需要把對象信息保存到存儲媒介中並之後讀取出來?
因爲二中的解釋,開發中有在JVM非運行的情況下或者在其他機器JVM上獲取指定Java對象的需求。


四、Android 中Serializable與Parcelable區別?

兩種都是用於支持序列化、反序列化話操作,兩者最大的區別在於存儲媒介的不同,Serializable使用IO讀寫存儲在硬盤上,而Parcelable是直接在內存中讀寫,很明顯內存的讀寫速度通常大於IO讀寫,所以在Android中通常優先選擇Parcelable。
Serializable不是當前關注的焦點,不過可以查看《Java序列化算法透析》這篇文章中實現一個簡單的Serializable例子,查看序列化生成的IO文件,並且以16進制讀取並一一解釋每一個16進制數字的含義。


五、Parcelable舉例

在Android中實現Parcelable接口的類可以支持序列與反序列化,以下是一個實現的舉例:
1. 實現Parcelable接口
2. 添加實體屬性
3. 覆寫writeToParcel(Parcel dest, int flags)方法,指定寫入Parcel類的數據。
4. 創建Parcelable.Creator靜態對象,有兩個方法createFromParcel(Parcel in)與newArray(int size),前者指定如何從Parcel中讀取出數據對象,後者創建一個數組。

5. 覆寫describeContents方法,默認返回0。

public class Gril implements Parcelable {
 
     private int mAge; // 年齡
     private boolean mSexy; // 是否性感
     
     @Override
     public void writeToParcel(Parcel dest, int flags) {
          dest.writeInt(mAge);
          dest.writeByte((byte) (mSexy ? 1 : 0));
     }
     
     public static final Parcelable.Creator<gril> CREATOR = new Parcelable.Creator<gril>() {
          public Gril createFromParcel(Parcel in) {
               Gril gril = new Gril();
               gril.mAge = in.readInt();
               gril.mSexy = in.readByte() != 0;
              return gril;
          }
          
          public Gril[] newArray(int size) {
              return new Gril[size];
          }
     };
     
     @Override
     public int describeContents() {
          return 0;
     }
}



六、Parcelable原理

從上面的例子中可以看出,具體的寫入(dest.writeInt(mAge);)與讀取(gril.mAge = in.readInt();)都是針對Parcel對象進行的操作,下面貼出的是Parcle 讀寫int類型數據的定義。

 

public final class Parcel {
 
    ......
     
    /**
     * Write an integer value into the parcel at the current dataPosition(),
     * growing dataCapacity() if needed.
     */
    public final native void writeInt(int val);
 
    /**
     * Read an integer value from the parcel at the current dataPosition().
     */
    public final native int readInt();
     
     ......
}


從上面代碼可以看出都是native方法說明都是使用JNI,其具體位置在system/frameworks/base/core/jni/android_util_Binder.cpp ,以下也僅以int類型讀寫爲例
static void android_os_Parcel_writeInt(JNIEnv* env, jobject clazz, jint val)
{
    Parcel* parcel = parcelForJavaObject(env, clazz);
    if (parcel != NULL) {
        const status_t err = parcel->writeInt32(val);
        if (err != NO_ERROR) {
            jniThrowException(env, java/lang/OutOfMemoryError, NULL);
        }
    }
}
 
static jint android_os_Parcel_readInt(JNIEnv* env, jobject clazz)
{
    Parcel* parcel = parcelForJavaObject(env, clazz);
    if (parcel != NULL) {
        return parcel->readInt32();
    }
    return 0;
}


從上面可以看出都會調用Parcel實現且分別調用writeInt32與readInt32函數,接着來看看具體實現。位置:/system/frameworks/base/libs/binder/Parcel.cpp
status_t Parcel::writeInt32(int32_t val)
{
    return writeAligned(val);
}
 
template<class t="">
status_t Parcel::writeAligned(T val) {
    COMPILE_TIME_ASSERT_FUNCTION_SCOPE(PAD_SIZE(sizeof(T)) == sizeof(T));
 
    if ((mDataPos+sizeof(val)) <= mDataCapacity) {
restart_write:
        *reinterpret_cast<t*>(mData+mDataPos) = val;
        return finishWrite(sizeof(val));
    }
 
    status_t err = growData(sizeof(val));
    if (err == NO_ERROR) goto restart_write;
    return err;
}
 
 
status_t Parcel::readInt32(int32_t *pArg) const
{
    return readAligned(pArg);
}
 
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 += sizeof(T);
        *pArg =  *reinterpret_cast<const>(data);
        return NO_ERROR;
    } else {
        return NOT_ENOUGH_DATA;
    }
}


以下4點摘自《探索Android中的Parcel機制(上)》
有興趣的朋友可以自己讀一下,不難理解,這裏把基本的思路總結一下:
1. 整個讀寫全是在內存中進行,主要是通過malloc()、realloc()、memcpy()等內存操作進行,所以效率比JAVA序列化中使用外部存儲器會高很多;
2. 讀寫時是4字節對齊的,可以看到#define PAD_SIZE(s) (((s)+3)&~3)這句宏定義就是在做這件事情;
3. 如果預分配的空間不夠時newSize = ((mDataSize+len)*3)/2;會一次多分配50%;

4. 對於普通數據,使用的是mData內存地址,對於IBinder類型的數據以及FileDescriptor使用的是mObjects內存地址。後者是通過flatten_binder()和unflatten_binder()實現的,目的是反序列化時讀出的對象就是原對象而不用重新new一個新對象。

 

七、序列化反序列化Parcelable實驗?

1. 任何實體類都需要複寫Parcelable接口嗎?
2. 如果子類新增屬性,需要複寫父類writeToParcel與CREATOR嗎?
3. writeToParcel 與 createFromParcel 對變量的讀寫前後順序可以不一致嗎,會出現什麼結果?
4. 讀寫Parcelable對象(寫操作dest.writeParcelable(obj, flags); 讀操作in.readParcelable(ObjectA.class.getClassLoader()); )

5. 讀寫Parcelable對象數組

dest.writeParcelableArray(mClassNameList.toArray(new ClassName[mClassNameList.size()]), flags);
 
Parcelable[] parcelableArr = in.readParcelableArray(ClassName.class.getClassLoader());
ArrayList<classname> arrayList = new ArrayList<classname>();
for (Parcelable object : parcelableArr) {
     arrayList.add((ClassName)object);
}


八、自己實現序列與反序列化機制

《C 語言的數據序列化 (C語言實現序列化機制的思路)》

原文地址:

http://www.2cto.com/kf/201403/288476.html

發佈了19 篇原創文章 · 獲贊 3 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章