Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android Serializable與Parcelable原理與區別

Android Serializable與Parcelable原理與區別

編輯:關於Android編程

 

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

(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 CREATOR = new Parcelable.Creator() {
          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
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(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
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(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 arrayList = new ArrayList();
for (Parcelable object : parcelableArr) {
     arrayList.add((ClassName)object);
}


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

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



 


  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved