Android Serializable與Parcelable原理與區別

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

(1) 名詞解釋

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

(2) 序列化詳細解釋

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

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

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

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


android

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

Java對象存在的前提必須在JVM運行期間存在,若是想在JVM非運行的狀況下或者在其餘機器JVM上獲取指定Java對象,在現有Java對象的機制下都不可能完成。
與Java對象不一樣的是,若是對Java對象執行序列化操做,由於原理是把Java對象信息保存到存儲媒介,因此能夠在以上Java對象不可能存在的兩種狀況下依然能夠使用Java對象。


算法

3、爲何要使用序列化、反序列化?

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


數組

4、Android 中Serializable與Parcelable區別?

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


ide

5、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



6、Parcelable原理

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

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

code

status_t Parcel::writeInt32(int32_t val)
{
    return writeAligned(val);
}

template


如下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一個新對象。

7、序列化反序列化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
相關文章
相關標籤/搜索