Android 對象序列化之 Parcelable 取代 Serializable ?

閃存
Android 存儲優(yōu)化系列專題
  • SharedPreferences 系列

Android 之不要濫用 SharedPreferences
Android 之不要濫用 SharedPreferences(2)— 數(shù)據(jù)丟失

  • ContentProvider 系列(待更)

《Android 存儲選項之 ContentProvider 啟動過程源碼分析》
《Android 存儲選項之 ContentProvider 深入分析》

  • 對象序列化系列

Android 對象序列化之你不知道的 Serializable
Android 對象序列化之 Parcelable 取代 Serializable ?
Android 對象序列化之追求性能完美的 Serial

  • 數(shù)據(jù)序列化系列(待更)

《Android 數(shù)據(jù)序列化之 JSON》
《Android 數(shù)據(jù)序列化之 Protocol Buffer 使用》
《Android 數(shù)據(jù)序列化之 Protocol Buffer 源碼分析》

  • SQLite 存儲系列

Android 存儲選項之 SQLiteDatabase 創(chuàng)建過程源碼分析
Android 存儲選項之 SQLiteDatabase 源碼分析
數(shù)據(jù)庫連接池 SQLiteConnectionPool 源碼分析
SQLiteDatabase 啟用事務(wù)源碼分析
SQLite 數(shù)據(jù)庫 WAL 模式工作原理簡介
SQLite 數(shù)據(jù)庫鎖機制與事務(wù)簡介
SQLite 數(shù)據(jù)庫優(yōu)化那些事兒


前言

對象序列化系列,主要內(nèi)容包括:Java 原生提供的 Serializable ,更加適合 Android 平臺輕量且高效的 Parcelable,以及追求性能完美的 Serial。該系列內(nèi)容主要結(jié)合源碼的角度分析它們各自的優(yōu)缺點以及合適的使用場景。

在上一篇文章《Android 對象序列化之你不知道的 Serializable》為大家介紹了 Serializable 的簡單實現(xiàn)機制背后復雜的計算邏輯,由于整個實現(xiàn)過程使用了大量反射和臨時變量,而且在序列化對象的時候,不僅需要序列化當前對象本身,還需要遞歸序列化引用的其他對象。

雖然 Serializable 性能那么差,但是它仍然有一些優(yōu)勢和進階的使用技巧,正如文中所述:“Java 原生提供的 Serializalbe 對象序列化機制,遠比大多數(shù) Java 開發(fā)人員想象的更靈活,這使得我們有更多的機會解決棘手的問題”。

說道這里如果你還對 Serializable 序列化機制不熟悉的話可以先去參考下。

Parcelable 產(chǎn)生的背景

由于 Java 的 Serializable 的性能較低,Android 需要重新設(shè)計一套更加輕量且高效的對象序列化和反序列化機制。Parcelable 正式在這個背景下產(chǎn)生的,它核心作用就是為了解決 Android 中大量跨進程通信的性能問題。

在時間開銷和使用成本的權(quán)衡上,Serializable 選擇了使用成本,而 Parcelable 機制則是選擇性能優(yōu)先。所以在它寫入和讀取數(shù)據(jù)都需要手動添加自定義代碼,使用起來相比 Serializable 會復雜很多。但是正因為這樣,Parcelable 才不需要采用反射的方式去實現(xiàn)序列化和反序列化(有少許反射,文中會分析到)。

Parcelable 實現(xiàn)機制分析

下面我們還是通過一個例子結(jié)合源碼的角度開始分析:

public final class WebParams implements Parcelable {

    public static final String EXTRA_PARAMS_KEY = "extra_params_key";

    private int age;
    private String name;
    private long serialUid;
    private char[] flag = new char[10];
    private byte[] like = new byte[10];

    public WebParams() {
    }

    protected WebParams(Parcel in) {
        this.age = in.readInt();
        this.name = in.readString();
        this.serialUid = in.readLong();
        in.readCharArray(flag);
        in.readByteArray(like);
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(age);
        dest.writeString(name);
        dest.writeLong(serialUid);
        dest.writeCharArray(flag);
        dest.writeByteArray(like);
    }

    @Override
    public int describeContents() {
        return 0;
    }

    public static final Creator<WebParams> CREATOR = new Creator<WebParams>() {
        @Override
        public WebParams createFromParcel(Parcel in) {
            return new WebParams(in);
        }

        @Override
        public WebParams[] newArray(int size) {
            return new WebParams[size];
        }
    };
}

通過啟動 Activity 過程分析 Parcelable 序列化過程:

private void serialParcelable() {
    final Intent intent = new Intent(this, null);
    final WebParams params = new WebParams();
    //...省略參數(shù)配置
    //通過設(shè)置附加參數(shù)到Intent中
    intent.putExtra(WebParams.EXTRA_PARAMS_KEY, params);
    startActivity(intent);
}

熟悉這一過程的朋友過程肯定知道,startActivity 方法最終會通過 AMS(ActivityManagerService)完成跨進程通信調(diào)用,但是在通信之前先要將數(shù)據(jù)序列化后進行傳輸,這個過程首先會調(diào)用 ActivityManagerProxy 的 startActivity 方法

public int startActivity(IApplicationThread caller, String callingPackage, Intent intent,
        String resolvedType, IBinder resultTo, String resultWho, int requestCode,
        int startFlags, ProfilerInfo profilerInfo, Bundle options) throws RemoteException {
    //負責寫出
    Parcel data = Parcel.obtain();
    //負責讀取
    Parcel reply = Parcel.obtain();
    data.writeInterfaceToken(IActivityManager.descriptor);
    data.writeStrongBinder(caller != null ? caller.asBinder() : null);
    data.writeString(callingPackage);
    //我們分析Parcelable序列化重點在這里
    intent.writeToParcel(data, 0);
    data.writeString(resolvedType);
    data.writeStrongBinder(resultTo);
    data.writeString(resultWho);
    data.writeInt(requestCode);
    data.writeInt(startFlags);
    if (profilerInfo != null) {
        data.writeInt(1);
        profilerInfo.writeToParcel(data, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
    } else {
        data.writeInt(0);
    }
    if (options != null) {
        data.writeInt(1);
        options.writeToParcel(data, 0);
    } else {
        data.writeInt(0);
    }
    //開始跨進程通信
    mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);
    reply.readException();
    int result = reply.readInt();
    reply.recycle();
    data.recycle();
    return result;
}

其中 intent.writeToParcel 是我們要重點跟蹤的方法,先來看下它的參數(shù) Parcel 類型,其實從這里的獲取方式大家也能猜測的出內(nèi)部使用了復用機制,就類似于 Message.obtain。

public static Parcel obtain() {
    //當前緩存池,sOwnedPool是一個靜態(tài)變量
    final Parcel[] pool = sOwnedPool;
    synchronized (pool) {
        Parcel p;
        //獲取可以被復用的Parcel, //POOL_SIZE默認大小為6
        for (int i = 0; i < POOL_SIZE; i++) {
            p = pool[i];
            if (p != null) {
                //獲取到復用對象,將該位置置為null
                pool[i] = null;
                if (DEBUG_RECYCLE) {
                    p.mStack = new RuntimeException();
                }
                //這是一個默認輔助讀寫
                p.mReadWriteHelper = ReadWriteHelper.DEFAULT;
                return p;
            }
        }
    }
    //無可復用,直接創(chuàng)建
    return new Parcel(0);
}

從 for 循環(huán)獲取可復用的 Parcel 過程,不知大家是否能夠看得出這一個隊列的數(shù)據(jù)結(jié)構(gòu)。

這里看下 POOL_ZISE 的定義:

private static final int POOL_SIZE = 6;

sOwnedPool 的定義:

private static final Parcel[] sOwnedPool = new Parcel[POOL_SIZE];

如果從復用池獲取不到則直接創(chuàng)建 Parcel:

private Parcel(long nativePtr) {
    if (DEBUG_RECYCLE) {
        mStack = new RuntimeException();
    }
    //初始化Parcel
    init(nativePtr);
}

//調(diào)用init
private void init(long nativePtr) {
    if (nativePtr != 0) {
        mNativePtr = nativePtr;
        mOwnsNativeParcelObject = false;
    } else {
        //此時傳遞為0,Parcel內(nèi)存區(qū)域由我們自己創(chuàng)建
        //Native層Parcel地址指針
        mNativePtr = nativeCreate();
        mOwnsNativeParcelObject = true;
    }
}

實際上 Parcel 的核心實現(xiàn)都在 Parcel.cpp,Java 層 Parcel 只是對 native 層接口的調(diào)用封裝,我們先看下 native 層 Parcel 的創(chuàng)建過程:

private static native long nativeCreate();

//jni注冊
{"nativeCreate", "()J",(void*)android_os_Parcel_create},

//nativeCreate的具體實現(xiàn)
static jlong android_os_Parcel_create(JNIEnv* env, jclass clazz)
{
   //創(chuàng)建native層Parcel對象
   Parcel* parcel = new Parcel();
   return reinterpret_cast<jlong>(parcel);
}

有復用就一定有回收的邏輯,看下 Parcel 的回收邏輯:

public final void recycle() {
    if (DEBUG_RECYCLE) mStack = null;
    //釋放其native內(nèi)存
    freeBuffer();

    final Parcel[] pool;
    //使用new Parcel() 默認為true
    //表示我們對它的生命周期負責
    if (mOwnsNativeParcelObject) {
        pool = sOwnedPool;
    } else {
        mNativePtr = 0;
        pool = sHolderPool;
    }

    synchronized (pool) {
        for (int i = 0; i < POOL_SIZE; i++) {
            //獲取可以被緩存的位置
            if (pool[i] == null) {
                pool[i] = this;
                return;
            }
        }
    }
}

繼續(xù)向下分析,執(zhí)行 intent.writeToParcel 將 Parcel 作為參數(shù),這里我們主要跟蹤下 Parcelable 的寫入過程,

由于采用 Intent 傳遞附加參數(shù)過程,最終都會保存到 Bundle 中,而 Bundle 用于實際存儲數(shù)據(jù)的則是通過 Map 完成的:

 //添加附加參數(shù)
 public @NonNull Intent putExtra(String name, Parcelable value) {
    if (mExtras == null) {
        //創(chuàng)建Bundle實例
        mExtras = new Bundle();
    }
    //實際保存在Bundle中
    mExtras.putParcelable(name, value);
    return this;
}

接著看下 Bundle 的保存附加數(shù)據(jù)過程:

public void putParcelable(@Nullable String key, @Nullable Parcelable value) {
    unparcel();
    //該mMap是一個ArrayMap實例
    mMap.put(key, value);
    mFlags &= ~FLAG_HAS_FDS_KNOWN;
}

此時將攜帶的附加參數(shù)進行序列化,實際是調(diào)用 Bundle 的 writeToParcel 方法,這里實際操作在其父類 BaseBundle 的 writeToParcelInner 方法中:

void writeToParcelInner(Parcel parcel, int flags) {
    if (parcel.hasReadWriteHelper()) {
        //這里(mReadWriteHelper != null) && (mReadWriteHelper != ReadWriteHelper.DEFAULT);
        //mReadWriteHelper默認是DEFAULT
        unparcel();
    }
    final ArrayMap<String, Object> map;
    synchronized (this) {
        if (mParcelledData != null) {
            //當前Bundle是否有被解析過
            if (mParcelledData == NoImagePreloadHolder.EMPTY_PARCEL) {
                parcel.writeInt(0);
            } else {
                int length = mParcelledData.dataSize();
                parcel.writeInt(length);
                parcel.writeInt(mParcelledByNative ? BUNDLE_MAGIC_NATIVE : BUNDLE_MAGIC);
                parcel.appendFrom(mParcelledData, 0, length);
            }
            return;
        }
        map = mMap;
    }

    if (map == null || map.size() <= 0) {
        parcel.writeInt(0);
        return;
    }
    //parcel當前內(nèi)存起始位置
    int lengthPos = parcel.dataPosition();
    //這里是附加參數(shù)的長度,臨時占位
    parcel.writeInt(-1); // dummy, will hold length
    //魔數(shù)
    parcel.writeInt(BUNDLE_MAGIC);
    //附加數(shù)據(jù)起始位置
    int startPos = parcel.dataPosition();
    //寫入當前所有的附加參數(shù)
    parcel.writeArrayMapInternal(map);
    //附加數(shù)據(jù)結(jié)束位置
    int endPos = parcel.dataPosition();
    //重新指到lengthPos
    parcel.setDataPosition(lengthPos);
    //此時可以計算出附加數(shù)據(jù)的真實長度
    int length = endPos - startPos;
    parcel.writeInt(length);
    //重新指到當前結(jié)束位置endpos
    parcel.setDataPosition(endPos);
}

代碼中已經(jīng)標注了詳細的注釋,這里我們重點看下 Parcelable 的序列化機制parcel.writeArrayMapInternal 方法:

void writeArrayMapInternal(ArrayMap<String, Object> val) {
    if (val == null) {
        writeInt(-1);
        return;
    }
    //附件參數(shù)的長度
    final int N = val.size();
    //寫入時Map的長度
    writeInt(N);
    int startPos;
    for (int i = 0; i < N; i++) {
        //寫入key
        writeString(val.keyAt(i));
        //寫入value,每個value類型都會額外浪費4字節(jié)(Int)
        writeValue(val.valueAt(i));
    }
}

寫入當前附加參數(shù)的總長度,遍歷 Map 容器,由于 key 是固定類型 String,這里我們重點關(guān)注下 writeValue 方法:

public final void writeValue(Object v) {
    if (v == null) {
        writeInt(VAL_NULL);
    } else if (v instanceof String) {
        //String類型
        writeInt(VAL_STRING);
        writeString((String) v);
    } else if (v instanceof Integer) {
        //Integer類型
        writeInt(VAL_INTEGER);
        writeInt((Integer) v);
    } else if (v instanceof Map) {
        //Map類型
        writeInt(VAL_MAP);
        writeMap((Map) v);
    } else if (v instanceof Bundle) {
        // Must be before Parcelable
        writeInt(VAL_BUNDLE);
        writeBundle((Bundle) v);
    } else if (v instanceof PersistableBundle) {
        writeInt(VAL_PERSISTABLEBUNDLE);
        writePersistableBundle((PersistableBundle) v);
    } else if (v instanceof Parcelable) {
        //Parcelable類型
        writeInt(VAL_PARCELABLE);
        writeParcelable((Parcelable) v, 0);
    } else if (v instanceof Short) {
        writeInt(VAL_SHORT);
        writeInt(((Short) v).intValue());
    } else if (v instanceof Long) {
        writeInt(VAL_LONG);
        writeLong((Long) v);
    }
    
    //... 省略
}

大家是否有注意到 Value 的寫入過程,系統(tǒng)自己定義了一套類型映射關(guān)系,根據(jù)Value 數(shù)據(jù)類型先寫四個字節(jié)的類型信息 writeInt(VAL_NULL)??聪逻@個類型映射關(guān)系:

private static final int VAL_NULL = -1;
private static final int VAL_STRING = 0;
private static final int VAL_INTEGER = 1;
private static final int VAL_MAP = 2;
private static final int VAL_BUNDLE = 3;
private static final int VAL_PARCELABLE = 4;
private static final int VAL_SHORT = 5;
private static final int VAL_LONG = 6;
private static final int VAL_FLOAT = 7;
private static final int VAL_DOUBLE = 8;
private static final int VAL_BOOLEAN = 9;
private static final int VAL_CHARSEQUENCE = 10;

//... 省略

這里不知大家是否有意識到,每個 Value 寫入都會額外附加 4 個字節(jié)的類型信息。用于表示當前 Value 的數(shù)據(jù)類型,這在后續(xù)反序列化時要根據(jù)該數(shù)據(jù)類型進行創(chuàng)建實例。

看下 Parcelable 的序列化過程 writeParcelable 方法:

public final void writeParcelable(Parcelable p, int parcelableFlags) {
    if (p == null) {
        writeString(null);
        return;
    }
    //寫入Parcelable的全限定名,反序列化時,需要根據(jù)該全限定名查找一個類:Classloader.loadClass
    writeParcelableCreator(p);
    //這里是否大家熟悉呢?其實回到了我們自定義的Parcelable中
    p.writeToParcel(this, parcelableFlags);
}

public final void writeParcelableCreator(Parcelable p) {
    //寫入Parceable的全限定名
    String name = p.getClass().getName();
    writeString(name);
}

其中 p.writeToParcel 是否感到熟悉呢?其實這里就是回到了我們自定義 WebParams 的 writeToParcel 方法中:

@Override
public void writeToParcel(Parcel dest, int flags){
    dest.writeInt(age);
    dest.writeString(name);
    dest.writeLong(serialUid);
    dest.writeCharArray(flag);
    dest.writeByteArray(like);
}

此時就是我們按照按照實際要序列化內(nèi)容寫入到 Parcel 內(nèi)存了。

分析到這里,實際上整個 Parcelable 數(shù)據(jù)序列化過程就已經(jīng)一幕了然了,Parcelable 只是一個序列化規(guī)則,它向開發(fā)人員暴露 Parcel 操作對象,自行寫入要序列化的數(shù)據(jù)。它的核心實現(xiàn)都在 native 層 Parcel.cpp,Java 層 Parcel 是對其接口的封裝。

下面來分析下 Parcelable 的反序列化過程:

final Bundle extra = getIntent().getExtras();
final WebParams params = extra.getParcelable(WebParams.EXTRA_PARAMS_KEY);

還是通過上面示例進行分析

@Nullable
public <T extends Parcelable> T getParcelable(@Nullable String key) {
    //解析Parcel數(shù)據(jù)
    unparcel();
    //解析數(shù)據(jù)會封裝在該map中
    Object o = mMap.get(key);
    if (o == null) {
        return null;
    }
    try {
        return (T) o;
    } catch (ClassCastException e) {
        typeWarning(key, o, "Parcelable", e);
        return null;
    }
}

這里我們重點跟蹤下 unparcel 方法:

void unparcel() {
    synchronized (this) {
        final Parcel source = mParcelledData;
        if (source != null) {
            //這里開始從Parcel讀取序列化的數(shù)據(jù)
            initializeFromParcelLocked(source, /*recycleParcel=*/ true, mParcelledByNative);
        } else {
            //...忽略
        }
    }
}

//在unparcel方法調(diào)用該方法
private void initializeFromParcelLocked(@NonNull Parcel parcelledData, boolean recycleParcel,
        boolean parcelledByNative) {

    //如果Parcel數(shù)據(jù)為空
    if (isEmptyParcel(parcelledData)) {
        if (mMap == null) {
            mMap = new ArrayMap<>(1);
        } else {
            //將Map中每個位置元素置為null,
            mMap.erase();
        }
        mParcelledData = null;
        mParcelledByNative = false;
        return;
    }
    //獲取附加參數(shù)的長度,這里對應寫入時Map的size
    final int count = parcelledData.readInt();
    if (count < 0) {
        return;
    }

    ArrayMap<String, Object> map = mMap;
    if (map == null) {
        //按照size創(chuàng)建ArrayMap
        map = new ArrayMap<>(count);
    } else {
        map.erase();
        //調(diào)整為新的長度
        map.ensureCapacity(count);
    }
    try {
        if (parcelledByNative) {
            parcelledData.readArrayMapSafelyInternal(map, count, mClassLoader);
        } else {
            parcelledData.readArrayMapInternal(map, count, mClassLoader);
        }
    } catch (BadParcelableException e) {
        if (sShouldDefuse) {
            map.erase();
        } else {
            throw e;
        }
    } finally {
        mMap = map;
        if (recycleParcel) {
            recycleParcel(parcelledData);
        }
        //解析過后一定置為null
        //避免后續(xù)get相關(guān)內(nèi)容時再次發(fā)生解析
        mParcelledData = null;
        mParcelledByNative = false;
    }
}

前面是一些列的數(shù)據(jù)校驗和緩存設(shè)置,這里我們重點分析下 parcelledData.readArrayMapInternal 方法:

void readArrayMapInternal(ArrayMap outVal, int N,
                                        ClassLoader loader) {
    if (DEBUG_ARRAY_MAP) {
        RuntimeException here = new RuntimeException("here");
        here.fillInStackTrace();
        Log.d(TAG, "Reading " + N + " ArrayMap entries", here);
    }
    int startPos;
    //根據(jù)寫入時Map長度
    while (N > 0) {
        if (DEBUG_ARRAY_MAP) startPos = dataPosition();
        //讀取key
        String key = readString();
        //讀取Value
        Object value = readValue(loader);
        //追加到ArrayMap中,可以直接理解成put(key, valu)
        //append系統(tǒng)留給自己使用的
        outVal.append(key, value);
        N--;
    }
    outVal.validate();
    //此時一系列讀取完畢之后,全部都保存在Bundle的Map中,
    //后續(xù)我們通過Bundle的get操作直接從該Map中獲取
}

還記得在前面分析寫入 Parcel 數(shù)據(jù)時,都是通過鍵值對的形式,key 是固定的 String 類型,所以讀取時也是先通過 readString 讀取 key,緊接著 readValue 方法讀取對應的 value:

 public final Object readValue(ClassLoader loader) {
    //先讀取類型
    int type = readInt();

    //根據(jù)value類型進行匹配
    switch (type) {
        case VAL_NULL:
            //null類型
            return null;

        case VAL_STRING:
            //String類型
            return readString();

        case VAL_INTEGER:
            //int類型
            return readInt();

        case VAL_MAP:
            //Map類型
            return readHashMap(loader);

        case VAL_PARCELABLE:
            //Parcelable類型讀取
            return readParcelable(loader);

        case VAL_SHORT:
            return (short) readInt();
            
            // ... 省略
    }
}

讀取 value 相比 key 要麻煩一些,前面分析序列化過程寫入 value 數(shù)據(jù)時,先寫入該 value 數(shù)據(jù)對應的 int 類型,該類型在反序列化時會用到,此時系統(tǒng)就是根據(jù)該 int 值對應的 value 類型反序列化對應數(shù)據(jù)。我們以 readParcelable 類型為例:

public final <T extends Parcelable> T readParcelable(ClassLoader loader) {
    //獲取對應Parcelable的Creator
    Parcelable.Creator<?> creator = readParcelableCreator(loader);
    if (creator == null) {
        return null;
    }
    if (creator instanceof Parcelable.ClassLoaderCreator<?>) {
        //Creator也可以是ClassLoaderCreator
        //ClassLoaderCreator是Creator的子類
        Parcelable.ClassLoaderCreator<?> classLoaderCreator =
                (Parcelable.ClassLoaderCreator<?>) creator;
        return (T) classLoaderCreator.createFromParcel(this, loader);
    }
    //直接通過creatorFromParcel創(chuàng)建對應Parcelable
    //此時已經(jīng)回到了自定義Parcelable中CREATOR內(nèi)部類的createFromParcel方法
    return (T) creator.createFromParcel(this);
}

先來看 Parcelable 的 CREATOR 的獲取方式:

public final Parcelable.Creator<?> readParcelableCreator(ClassLoader loader) {
    String name = readString();
    if (name == null) {
        return null;
    }
    Parcelable.Creator<?> creator;
    synchronized (mCreators) {
        //系統(tǒng)根據(jù)ClassLoader緩存Parcelable的Creator
        //獲取當前類加載器緩存過的Parcelable的Creator實例
        HashMap<String, Parcelable.Creator<?>> map = mCreators.get(loader);
        if (map == null) {
            map = new HashMap<>();
            mCreators.put(loader, map);
        }
        //緩存中是否存在
        creator = map.get(name);
        if (creator == null) {
            try {
                ClassLoader parcelableClassLoader =
                        (loader == null ? getClass().getClassLoader() : loader);
                //反射獲取該類對象
                Class<?> parcelableClass = Class.forName(name, false /* initialize */,
                        parcelableClassLoader);
                if (!Parcelable.class.isAssignableFrom(parcelableClass)) {
                    //必須是Parcelable類型
                    throw new BadParcelableException("Parcelable protocol requires subclassing "
                            + "from Parcelable on class " + name);
                }
                //反射獲取Parcelable中CREATOR Field
                Field f = parcelableClass.getField("CREATOR");
                if ((f.getModifiers() & Modifier.STATIC) == 0) {
                    //必須是static的
                    throw new BadParcelableException("Parcelable protocol requires "
                            + "the CREATOR object to be static on class " + name);
                }
                Class<?> creatorType = f.getType();
                if (!Parcelable.Creator.class.isAssignableFrom(creatorType)) {
                    //必須是Parcelable.Creator類型
                    throw new BadParcelableException("Parcelable protocol requires a "
                            + "Parcelable.Creator object called "
                            + "CREATOR on class " + name);
                }
                //獲取到該Parcelable對應的Creator實例。
                creator = (Parcelable.Creator<?>) f.get(null);
            } catch (IllegalAccessException e) {
                throw new BadParcelableException(
                        "IllegalAccessException when unmarshalling: " + name);
            } catch (ClassNotFoundException e) {
                throw new BadParcelableException(
                        "ClassNotFoundException when unmarshalling: " + name);
            } catch (NoSuchFieldException e) {
                throw new BadParcelableException("Parcelable protocol requires a "
                        + "Parcelable.Creator object called "
                        + "CREATOR on class " + name);
            }
            if (creator == null) {
                throw new BadParcelableException("Parcelable protocol requires a "
                        + "non-null Parcelable.Creator object called "
                        + "CREATOR on class " + name);
            }
            //注意,系統(tǒng)緩存每個使用到的Parcelable的Creator實例
            //這樣下次創(chuàng)建對應的Parcelable時,直接通過Creator實例createFromParcel創(chuàng)建,
            //避免了再次反射
            map.put(name, creator);
        }
    }
    return creator;
}

系統(tǒng)首先根據(jù) Classloader(不同的 Classloader 加載的 Class 對象不相等) 獲取保存 CREATOR 的 Map 容器,然后根據(jù) value 類型的全限定名在該 Map 中查找是否已經(jīng)存在對應的 CREATOR 實例,否則通過 Classloader 加載該類,并反射獲取該類的 CREATOR 字段;

從這里我們可以看出:Parcelable 中為什么要包含一個 CREATOR 的字段,并且一定要聲明為 static,而且系統(tǒng)會緩存每個已經(jīng)使用過的 Parcelable 的 CREATOR 實例,便于下次反序列化時直接通過 new 創(chuàng)建 該 Parcelable 實例。

回到 readParcelable 方法:直接調(diào)用 CREATOR 的 createFromParcel 方法,此時就回到了我們自定義的 WebParams 中:

@Override
    public WebParams createFromParcel(Parcel in) {
        return new WebParams(in);
    }

然后在 WebParams 的構(gòu)造方法中根據(jù)寫入時順序?qū)⑵滟x值給 WebParams 成員:

protected WebParams(Parcel in) {
    this.age = in.readInt();
    this.name = in.readString();
    this.serialUid = in.readLong();
    in.readCharArray(flag);
    in.readByteArray(like);
}

至于為什么一定要按照順序讀取寫入時,通過分析源碼,相信大家也一定能夠理解,數(shù)據(jù)是按照順序進行排列的。至此關(guān)于 Parcelable 的序列化以及反序列化的 Java 層部分就分析完了。

通過分析我們發(fā)現(xiàn) Android 中 Parcelable 序列化和 Java 原生提供的 Serializalbe 序列化機制差別還是比較大的,Parcelable 只會在內(nèi)存中序列化操作,并不會將數(shù)據(jù)存儲到磁盤里。一般來說,如果需要持久化存儲的話,一般還是不得不選擇性能更差的 Serializable 方案,那有沒有其它選擇呢?你可以參考下篇文章《Android 對象序列化之追求性能完美的 Serial》。

總結(jié)

Parcelable 的原理還是比較簡單的,它的核心實現(xiàn)都在 Parcel.cpp。

正如前面給大家講到:Parcelable 時間開銷和使用成本的權(quán)衡上,Parcelable 機制選擇的是性能優(yōu)先。所以它在寫入和讀取的時候,都需要手動添加自定義代碼,使用起來相比 Serializable 會復雜很多。但是正因為這樣,Parcelable 才不需要采用反射的方式去實現(xiàn)序列化和反序列化(當然,實際 Parcelable 也用到了少許反射,這里是相對 Serialzable 而言微不足道)

Parcelable 的持久化存儲

雖然 Parcelable 默認不支持持久化存儲,但是我們也可以通過一些取巧的方式實現(xiàn),在 Parcel.java 中 marshall 接口獲取 byte 數(shù)組,然后存儲在文件中從而實現(xiàn) Parcelable 的永久存儲。

public final byte[] marshall() {
    return nativeMarshall(mNativePtr);
}

但是它也存在一些問題:

  1. 系統(tǒng)版本的兼容性。由于 Parcelable 設(shè)計本意是在內(nèi)存中使用,我們無法保證所有 Android 版本的 Parcel.cpp 實現(xiàn)都完全一致。如果不同系統(tǒng)版本實現(xiàn)有所差異i,或者廠商修改了實現(xiàn),可能會存在問題。
  2. 數(shù)據(jù)前后兼容性。Parcelable 并沒有版本管理的設(shè)計,如果我們類的版本出現(xiàn)升級,寫入的順序及字段類型的兼容都需要格外注意,這也帶來了很大的維護成本。

一般來說,如果需要持久化存儲的話,一般還是不得不選擇性能更差的 Serializable 方案。


以上便是個人在學習 Serial 時的心得和體會,文中分析如有不妥或更好的分析結(jié)果,還請大家指出!

文章如果對你有幫助,就請留個贊吧!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容