
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);
}
但是它也存在一些問題:
- 系統(tǒng)版本的兼容性。由于 Parcelable 設(shè)計本意是在內(nèi)存中使用,我們無法保證所有 Android 版本的 Parcel.cpp 實現(xiàn)都完全一致。如果不同系統(tǒng)版本實現(xiàn)有所差異i,或者廠商修改了實現(xiàn),可能會存在問題。
- 數(shù)據(jù)前后兼容性。Parcelable 并沒有版本管理的設(shè)計,如果我們類的版本出現(xiàn)升級,寫入的順序及字段類型的兼容都需要格外注意,這也帶來了很大的維護成本。
一般來說,如果需要持久化存儲的話,一般還是不得不選擇性能更差的 Serializable 方案。
以上便是個人在學習 Serial 時的心得和體會,文中分析如有不妥或更好的分析結(jié)果,還請大家指出!
文章如果對你有幫助,就請留個贊吧!