由于 Java 的 Serializable 的性能较低,Parcelable 正式在这个背景下产生的,它核心作用就是为了解决 Android 中大量跨进程通信的性能问题。
Serializable使用大量反射和临时变量,而Parcelable少许反射
通过启动 Activity 过程分析 Parcelable 序列化过程:
熟悉这一过程的朋友过程肯定知道,startActivity 方法最终会通过 AMS(ActivityManagerService)完成跨进程通信调用,但是在通信之前先要将数据序列化后进行传输
代码语言:javascript复制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 是我们要重点跟踪的方法,先来看下它的参数 Parcel 类型,其实从这里的获取方式大家也能猜测的出内部使用了复用机制,就类似于 Message.obtain。
代码语言:javascript复制public static Parcel obtain() {
//当前缓存池,sOwnedPool是一个静态变量
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;
}
}
}
//无可复用,直接创建
return new Parcel(0);
}
从 for 循环获取可复用的 Parcel 过程,不知大家是否能够看得出这一个队列的数据结构。如果从复用池获取不到则直接创建 Parcel
代码语言:javascript复制private Parcel(long nativePtr) {
if (DEBUG_RECYCLE) {
mStack = new RuntimeException();
}
//初始化Parcel
init(nativePtr);
}
//调用init
private void init(long nativePtr) {
if (nativePtr != 0) {
mNativePtr = nativePtr;
mOwnsNativeParcelObject = false;
} else {
//此时传递为0,Parcel内存区域由我们自己创建
//Native层Parcel地址指针
mNativePtr = nativeCreate();
mOwnsNativeParcelObject = true;
}
}
实际上 Parcel 的核心实现都在 Parcel.cpp,Java 层 Parcel 只是对 native 层接口的调用封装,我们先看下 native 层 Parcel 的创建过程:
代码语言:javascript复制private static native long nativeCreate();
//jni注册
{"nativeCreate", "()J",(void*)android_os_Parcel_create},
//nativeCreate的具体实现
static jlong android_os_Parcel_create(JNIEnv* env, jclass clazz)
{
//创建native层Parcel对象
Parcel* parcel = new Parcel();
return reinterpret_cast<jlong>(parcel);
}
有复用就一定有回收的逻辑,看下 Parcel 的回收逻辑:
代码语言:javascript复制public final void recycle() {
if (DEBUG_RECYCLE) mStack = null;
//释放其native内存
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;
}
}
}
}
执行 intent.writeToParcel 将 Parcel 作为参数,由于采用 Intent 传递附加参数过程,最终都会保存到 Bundle 中,而 Bundle 用于实际存储数据的则是通过 Map 完成的:
代码语言:javascript复制 //添加附加参数
public @NonNull Intent putExtra(String name, Parcelable value) {
if (mExtras == null) {
//创建Bundle实例
mExtras = new Bundle();
}
//实际保存在Bundle中
mExtras.putParcelable(name, value);
return this;
}
代码语言:javascript复制public void putParcelable(@Nullable String key, @Nullable Parcelable value) {
unparcel();
//该mMap是一个ArrayMap实例
mMap.put(key, value);
mFlags &= ~FLAG_HAS_FDS_KNOWN;
}
startactivity源码中会调用writeToParcel 方法,这里实际操作在其父类 BaseBundle 的 writeToParcelInner 方法中,其中重点是 Parcelable 的序列化机制parcel.writeArrayMapInternal 方法
代码语言:javascript复制void writeArrayMapInternal(ArrayMap<String, Object> val) {
if (val == null) {
writeInt(-1);
return;
}
//附件参数的长度
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字节(Int)
writeValue(val.valueAt(i));
}
}
写入当前附加参数的总长度,遍历 Map 容器,由于 key 是固定类型 String,这里我们重点关注下 writeValue 方法:
代码语言:javascript复制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 的写入过程,系统自己定义了一套类型映射关系,每个 Value 写入都会额外附加 4 个字节的类型信息。用于表示当前 Value 的数据类型,这在后续反序列化时要根据该数据类型进行创建实例。
看下 Parcelable 的序列化过程 writeParcelable 方法:
代码语言:javascript复制public final void writeParcelable(Parcelable p, int parcelableFlags) {
if (p == null) {
writeString(null);
return;
}
//写入Parcelable的全限定名,反序列化时,需要根据该全限定名查找一个类: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 是否感到熟悉呢?其实这里就是回到了我们自定义 Parcelable 的 writeToParcel 方法中:
代码语言:javascript复制@Override
public void writeToParcel(Parcel dest, int flags){
dest.writeInt(age);
dest.writeString(name);
dest.writeLong(serialUid);
dest.writeCharArray(flag);
dest.writeByteArray(like);
}
此时就是我们按照按照实际要序列化内容写入到 Parcel 内存了。
Parcelable 只是一个序列化规则,它向开发人员暴露 Parcel 操作对象,自行写入要序列化的数据。它的核心实现都在 native 层 Parcel.cpp,Java 层 Parcel 是对其接口的封装。
Parcelable 的反序列化过程 使用例子:
代码语言:javascript复制final Bundle extra = getIntent().getExtras();
final WebParams params = extra.getParcelable(WebParams.EXTRA_PARAMS_KEY);
还是通过上面示例进行分析
代码语言:javascript复制@Nullable
public <T extends Parcelable> T getParcelable(@Nullable String key) {
//解析Parcel数据
unparcel();
//解析数据会封装在该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方法解析Parcel数据
代码语言:javascript复制void unparcel() {
synchronized (this) {
final Parcel source = mParcelledData;
if (source != null) {
//这里开始从Parcel读取序列化的数据
initializeFromParcelLocked(source, /*recycleParcel=*/ true, mParcelledByNative);
} else {
//...忽略
}
}
}
//在unparcel方法调用该方法
private void initializeFromParcelLocked(@NonNull Parcel parcelledData, boolean recycleParcel,
boolean parcelledByNative) {
//如果Parcel数据为空
if (isEmptyParcel(parcelledData)) {
if (mMap == null) {
mMap = new ArrayMap<>(1);
} else {
//将Map中每个位置元素置为null,
mMap.erase();
}
mParcelledData = null;
mParcelledByNative = false;
return;
}
//获取附加参数的长度,这里对应写入时Map的size
final int count = parcelledData.readInt();
if (count < 0) {
return;
}
ArrayMap<String, Object> map = mMap;
if (map == null) {
//按照size创建ArrayMap
map = new ArrayMap<>(count);
} else {
map.erase();
//调整为新的长度
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
//避免后续get相关内容时再次发生解析
mParcelledData = null;
mParcelledByNative = false;
}
}
重点在于parcelledData.readArrayMapInternal 方法
代码语言:javascript复制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;
//根据写入时Map长度
while (N > 0) {
if (DEBUG_ARRAY_MAP) startPos = dataPosition();
//读取key
String key = readString();
//读取Value
Object value = readValue(loader);
//追加到ArrayMap中,可以直接理解成put(key, valu)
//append系统留给自己使用的
outVal.append(key, value);
N--;
}
outVal.validate();
//此时一系列读取完毕之后,全部都保存在Bundle的Map中,
//后续我们通过Bundle的get操作直接从该Map中获取
}
写入 Parcel 数据时,都是通过键值对的形式,key 是固定的 String 类型,所以读取时也是先通过 readString 读取 key,紧接着 readValue 方法读取对应的 value:
前面分析**序列化过程写入 value 数据时,先写入该 value 数据对应的 int 类型,该类型在反序列化时会用到,此时系统就是根据该 int 值对应的 value 类型反序列化对应数据。**我们以 readParcelable 类型为例:
代码语言:javascript复制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创建对应Parcelable
//此时已经回到了自定义Parcelable中CREATOR内部类的createFromParcel方法
return (T) creator.createFromParcel(this);
}
先来看 Parcelable 的 CREATOR 的获取方式:
代码语言:javascript复制public final Parcelable.Creator<?> readParcelableCreator(ClassLoader loader) {
String name = readString();
if (name == null) {
return null;
}
Parcelable.Creator<?> creator;
synchronized (mCreators) {
//系统根据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);
}
//注意,系统缓存每个使用到的Parcelable的Creator实例
//这样下次创建对应的Parcelable时,直接通过Creator实例createFromParcel创建,
//避免了再次反射
map.put(name, creator);
}
}
return creator;
}
系统首先根据 Classloader(不同的 Classloader 加载的 Class 对象不相等) 获取保存 CREATOR 的 Map 容器,然后根据 value 类型的全限定名在该 Map 中查找是否已经存在对应的 CREATOR 实例,否则通过 Classloader 加载该类,并反射获取该类的 CREATOR 字段;
从这里我们可以看出:Parcelable 中为什么要包含一个 CREATOR 的字段,并且一定要声明为 static,而且系统会缓存每个已经使用过的 Parcelable 的 CREATOR 实例,便于下次反序列化时直接通过 new 创建 该 Parcelable 实例。
回到 readParcelable 方法:直接调用 CREATOR 的 createFromParcel 方法,此时就回到了我们自定义的 WebParams 中:
代码语言:javascript复制@Override
public WebParams createFromParcel(Parcel in) {
return new WebParams(in);
}
代码语言:javascript复制protected WebParams(Parcel in) {
this.age = in.readInt();
this.name = in.readString();
this.serialUid = in.readLong();
in.readCharArray(flag);
in.readByteArray(like);
}
Parcelable 只会在内存中序列化操作,并不会将数据存储到磁盘里。一般来说,如果需要持久化存储的话,一般还是不得不选择性能更差的 Serializable 方案
虽然 Parcelable 默认不支持持久化存储,但是我们也可以通过一些取巧的方式实现,在 Parcel.java 中 marshall 接口获取 byte 数组,然后存储在文件中从而实现 Parcelable 的永久存储。