1 简介
在Android组件间或者跨进程组件间要传递数据都是通过使用 Intent.putExtra() 或者 Bundle.putXXXExtra()方法进行,这些方法无法支持传递对象的引用,而只支持基本平台类型和实现了Serializable或者Parcelable接口的类对象。
Serializable接口我们在《Android序列化(一) 之 Serializable》中已经介绍过,它是Java提供的可将对象序列化成字节流进行文件持久化存储或网络传输,也可将字节流反序列化成新的对象。
Parcelable接口在android.os包中,实现它便能让对象支持Parcel。简单地理解就是实现Parcelable接口便能使一个自定义类在Android组件间传递具有序列化和反序列化的能力。
Parcel类也在android.os包中,它不是通用序列化机制,而是一种可通过IBinder高性能传输数据和对象的容器,或者说是它是Android里特殊序列化机制。Parcel内部采用共享内存的方式实现用户空间和内核空间的数据交换,本质是Native层的共享内存。
凡通过Parcel包装后的数据都可以通过在Binder进程间通信IPC中进行端和端的数据交互,一端将数据Parcel化后写入到一个共享内存中,另一端通过Parcel可以从这块共享内存中读出字节流还原成原来的数据。Parcel支持基础平台数据类型,也支持实现了Parcelable接口的对象,也支持一个活动的IBinder对象的引用,这个引用导致另一端接收到一个指向这个IBinder的代理IBinder。在Android里AIDL也用到了Parcel进行数据封装。
2 示例
public class Student implements Parcelable {
private int id;
private String name;
private boolean verified;
private Phone mainPhone;
private List phones;
private Map tags;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void setVerified(boolean verified) {
this.verified = verified;
}
public boolean isVerified() {
return verified;
}
public Phone getMainPhone() {
return mainPhone;
}
public void setMainPhone(Phone phone) {
this.mainPhone = phone;
}
public List getPhones() {
return phones;
}
public void setPhones(List phones) {
this.phones = phones;
}
public Map getTags() {
return tags;
}
public void setTags(Map tags) {
this.tags = tags;
}
public Student() {
}
private Student(Parcel in) {
id = in.readInt();
name = in.readString();
verified = in.readByte() == 1; // API level 29后可以直接使用 readBoolean 支持 Boolean类型
mainPhone = in.readParcelable(Phone.class.getClassLoader());
phones = in.readArrayList(Phone.class.getClassLoader());
// 另外一种方式读取List,需要List先初始化
// phones = new ArrayList<>();
// in.readList(phones, Phone.class.getClassLoader());
// 另外一种方式读取List,但是需要配合 write 时使用了 writeTypedList,这里才应该使用 createTypedArrayList 传入 CREATOR
// phones = in.createTypedArrayList(Phone.CREATOR);
// 另外一种方式读取List,但是需要配合 write 时使用了 writeTypedList,这里才应该使用 readTypedList 传入 CREATOR
// 而且需要List先初始化
// phones = new ArrayList<>();
// in.readTypedList(phones, Phone.CREATOR);
// 如果Map内是基本数据类型,传入的ClassLoader可为null,
// 如果key或value其中一个或两个都是同一个实现Parcelable的类,传入对应类的ClassLoader,
// 如果key和value两个是不同的类,传什么都不对
tags = in.readHashMap(null);
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(id);
dest.writeString(name);
dest.writeByte((byte)(verified? 1: 0)); // API level 29后可以直接使用 writeBoolean 支持 Boolean类型
dest.writeParcelable(mainPhone, 0);
dest.writeList(phones);
// 另一种序列化方式,反序列化时需要使用 createTypedArrayList 或 readTypedList
// dest.writeTypedList(phones);
dest.writeMap(tags);
}
public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
public Student createFromParcel(Parcel in) {
return new Student(in);
}
public Student[] newArray(int size) {
return new Student[size];
}
};
static class Phone implements Parcelable {
private String number;
private String type;
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public Phone() {
}
private Phone(Parcel in) {
number = in.readString();
type = in.readString();
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(number);
dest.writeString(type);
}
public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
public Phone createFromParcel(Parcel in) {
return new Phone(in);
}
public Phone[] newArray(int size) {
return new Phone[size];
}
};
}
}
数据发送方:
Student student = new Student();
student.setId(1001);
student.setName("子云心");
student.setVerified(true);
Student.Phone mainPhone = new Student.Phone();
mainPhone.setType("MAIN");
mainPhone.setNumber("99999999999");
student.setMainPhone(mainPhone);
List phones = new ArrayList<>();
Student.Phone phone1 = new Student.Phone();
phone1.setType("MOBILE");
phone1.setNumber("12345678910");
phones.add(phone1);
Student.Phone phone2 = new Student.Phone();
phone2.setType("HOME");
phone2.setNumber("0000-1234567");
phones.add(phone2);
student.setPhones(phones);
Map tags = new HashMap();
tags.put(0, "三好学生");
tags.put(1, "体育达人");
student.setTags(tags);
Intent intent = new Intent(MainActivity.this, MainActivity2.class);
intent.putExtra("studentData", student);
// 使用 Bundle 也可以,intent.putExtra内部也是使用了Bundle
Bundle args = new Bundle();
args.putParcelable("studentData2", student);
intent.putExtra("bundleData", args);
startActivity(intent);
数据接收方:
Student student = getIntent().getParcelableExtra("studentData");
Bundle bundle = getIntent().getBundleExtra("bundleData");
Student student2 = bundle.getParcelable("studentData2");
3 类的结构
实现Parcelable接口的类,必须要重写describeContents 和 writeToParcel 两个方法,必须有一个参数是Parcel类型的构造方法,以及必须要声明一个非空的、名字必须是CREATOR的、类型是Parcelable.Creator
3.1 describeContents方法
目前describeContents方法的返回值要么是0,要么是Parcelable.CONTENTS_FILE_DEscriptOR(0x0001)。Parcelable.CONTENTS_FILE_DEscriptOR表示Paracelable里面含一些特殊的文件描述信息。在大多数情况下返回0即可。
3.2 writeToParcel方法 和 带Parcel参数的构造方法
writeToParcel方法是实现序列化写操作的地方,它返回一个Parcel对象。带Parcel参数的构造方法是实现反序列化读操作的地方,它接收一个Parcel对象。Parcel对象的写入和读取的顺序必须保持一致,否则会发生数值错误或者类型异常崩溃情况。序列化和反序列化由一系列Parcel的write和read方法支持,例如常用的方法有:
byte类型
序列化方法: void writeByte(byte val)
反序列化方法: byte readByte()
boolean类型(API level 29后才支持,在之前可以使用byte是1或者0来代替,如上面示例)
序列化方法: void writeBoolean(boolean val)
反序列化方法: boolean readBoolean()
int类型
序列化方法: void writeInt(int val)
反序列化方法: int readInt()
String类型
序列化方法: void writeString(String val)
反序列化方法: String readString()
等等一系列基础平台类型
序列化方法: void writeXXX(XXX val)
反序列化方法: XXX readXXX()
实现了Parcelable的类
序列化方法: void writeParcelable(@Nullable Parcelable p, int parcelableFlags)
反序列化方法:
Map
序列化方法: void writeMap(@Nullable Map val)
反序列化方法1,内部会new一个新的HashMap对象:
HashMap readHashMap(@Nullable ClassLoader loader)
反序列化方法2,内部不会new出新对象,输入的outVal必须提前初始化:
void readMap(@NonNull Map outVal, @Nullable ClassLoader loader)
List
序列化方法: void writeList(@Nullable List val)
反序列化方法1,内部会new一个新的ArrayList对象:
ArrayList readArrayList(@Nullable ClassLoader loader)
反序列化方法2,内部不会new出新对象,输入的outVal必须提前初始化:
void readList(@NonNull List outVal, @Nullable ClassLoader loader)
List
序列化方法:
反序列化方法1,内部会new一个新的ArrayList对象:
反序列化方法2,内部不会new出新对象,输入的outVal必须提前初始化:
3.3 CREATOR变量
CREATOR变量用于从Parcel中取出指定的数据类型,它是一个非空的、名字必须是CREATOR的、类型是Parcelable.Creator
4 startActivity通过Intent传递参数过程
根据上面示例得知,可以通过Intent.putExtra或者Bundle.putParcelable将实现了Parcelable接口的对象添加到Intent中,从而进行组件间数据传递。Intent.putExtra其实也是调用了Bundle.putBundle。
Intent.java
public @NonNull Intent putExtra(String name, @Nullable Bundle value) {
if (mExtras == null) {
mExtras = new Bundle();
}
mExtras.putBundle(name, value);
return this;
}
Bundle.java
public void putBundle(@Nullable String key, @Nullable Bundle value) {
unparcel();
mMap.put(key, value);
}
public void putParcelable(@Nullable String key, @Nullable Parcelable value) {
unparcel();
mMap.put(key, value);
mFlags &= ~FLAG_HAS_FDS_KNOWN;
}
无论是Bundle.putBundle还是Bundle.putParcelable,都是先调用父类的unparcel方法。
baseBundle.java
void unparcel() {
synchronized (this) {
final Parcel source = mParcelledData;
if (source != null) {
initializeFromParcelLocked(source, true, mParcelledByNative);
} else {
if (DEBUG) {
Log.d(TAG, "unparcel "
+ Integer.toHexString(System.identityHashCode(this))
+ ": no parcelled data");
}
}
}
}
unparcel方法内,此时mParcelledData对象还是为null状态(mParcelledData下面会进行介绍),所以unparcel方法此时如果是DEBUG则只是打印了一行日志而已。
在调用unparcel方法后putBundle和putParcelable都是将对象添加到一个ArrayMap
在进行startActivity时就会将Intent对象传入,startActivity实质是会进行一次AMS(Android10以下是ActivityManagerService/AMS,Android10之后是ActivityTaskManagerService/ATMS)的跨进程通信,这时就会将Intent内的数据也一同传递过去,
Activity.java
public void startActivityForResult(@RequiresPermission Intent intent, int requestCode, @Nullable Bundle options) {
……
Instrumentation.ActivityResult ar = mInstrumentation.execStartActivity(this, mMainThread.getApplicationThread(), mToken, this, intent, requestCode, options);
……
}
Instrumentation.java(源码基于Android10,使用的是ATMS)
public ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options) {
……
int result = ActivityTaskManager.getService().startActivity(whoThread,
who.getbasePackageName(), who.getAttributionTag(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()), token,
target != null ? target.mEmbeddedID : null, requestCode, 0, null, options);
……
}
ActivityTaskManager.java
public static IActivityTaskManager getService() {
return IActivityTaskManagerSingleton.get();
}
private static final Singleton IActivityTaskManagerSingleton = new Singleton() {
@Override
protected IActivityTaskManager create() {
final IBinder b = ServiceManager.getService(Context.ACTIVITY_TASK_SERVICE);
return IActivityTaskManager.Stub.asInterface(b);
}
};
ActivityTaskManager.getService()返回的是实现了IActivityTaskManager接口的ActivityTaskManagerService。IActivityTaskManager位于android/app/IActivityTaskManager.aidl,它没有对应的Java代码,它是一个AIDL文件,返回的结果是 Binder 的代理类 , 该类主要作用是使用了Binder机制 , 进行进程间通信。
IActivityTaskManager生成的Java源码我们无法查看到,但能想像到进行IPC时传入的Intent肯定是先进行序列化的,所以我们可以进行模拟创建一个AILD并在接口方法参数传入一个Intent对象。如:
自定义的IActivityTaskManager.aidl,它是一段模拟IActivityTaskManager的伪代码
package com.zyx.myapplication;
interface IActivityTaskManager {
int startActivity(in Intent intent);
}
使用AndroidStudio编译自动生成IActivityTaskManager.java
public interface IActivityTaskManager extends android.os.IInterface {
……
public static abstract class Stub extends android.os.Binder implements com.zyx.myapplication.IActivityTaskManager {
……
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
java.lang.String descriptor = DEscriptOR;
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(descriptor);
return true;
}
case TRANSACTION_startActivity: {
data.enforceInterface(descriptor);
android.content.Intent _arg0;
if ((0!=data.readInt())) {
// 关键代码3,这里传入序列化后的数据创建一个新的Inetnt对象
_arg0 = android.content.Intent.CREATOR.createFromParcel(data);
}
else {
_arg0 = null;
}
int _result = this.startActivity(_arg0);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
default: {
return super.onTransact(code, data, reply, flags);
}
}
}
private static class Proxy implements com.zyx.myapplication.IActivityTaskManager {
……
private android.os.IBinder mRemote;
@Override public int startActivity(android.content.Intent intent) throws android.os.RemoteException {
// 关键代码1,创建2个Parcel对象,用于记录序列化数据和序列化结果
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DEscriptOR);
if ((intent!=null)) {
_data.writeInt(1);
// 关键代码2,这里进行了序列化的操作
intent.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
boolean _status = mRemote.transact(Stub.TRANSACTION_startActivity, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
return getDefaultImpl().startActivity(intent);
}
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
……
}
……
}
……
}
从生成的代码可见:
关键代码1中,IActivityTaskManager$Stub$Proxy#startActivity方法开始先通过Parcel的obtain方法先创建两个Parcel对象,用于后面记录序列化数据和序列化结果。
关键代码2中,IActivityTaskManager$Stub$Proxy#startActivity方法传入的Intent参数进行调用Intent的writeToParcel方法,该方法就是序列化的开始(下面会介绍内在过程),序列化的结果通过_data输出,最后通过mRemote.transact将序列化后的_data传递到ActivityTaskManagerService进程去。
从AIDL的运行原理得知,当Client端发起跨进程请求时,远程请求会通过系统底层封装后交由onTransact方法来处理。onTransact方法判断传入的code,即在关键代码2后传入的 TRANSACTION_startActivity 找到关健代码3的地方,关键代码3中,传入将序列化后的数据通过 Intent.CREATOR.createFromParcel创建一个新的Inetnt对象。
Intent.java
public static final @android.annotation.NonNull Parcelable.CreatorCREATOR = new Parcelable.Creator () { public Intent createFromParcel(Parcel in) { return new Intent(in); } public Intent[] newArray(int size) { return new Intent[size]; } }; protected Intent(Parcel in) { readFromParcel(in); } public void readFromParcel(Parcel in) { setAction(in.readString8()); mData = Uri.CREATOR.createFromParcel(in); mType = in.readString8(); mIdentifier = in.readString8(); mFlags = in.readInt(); mPackage = in.readString8(); mComponent = ComponentName.readFromParcel(in); …… mExtras = in.readBundle(); }
readFromParcel方法中,关键是最后一行,readBundle 用于创建一个新的Bundle对象赋予给mExtras变量。
Parcel.java
public final Bundle readBundle(@Nullable ClassLoader loader) {
int length = readInt();
……
final Bundle bundle = new Bundle(this, length);
if (loader != null) {
bundle.setClassLoader(loader);
}
return bundle;
}
Bundle.java
public Bundle(Parcel parcelledData, int length) {
super(parcelledData, length);
mFlags = FLAG_ALLOW_FDS;
maybePrefillHasFds();
}
baseBundle.java
baseBundle(Parcel parcelledData, int length) {
readFromParcelInner(parcelledData, length);
}
private void readFromParcelInner(Parcel parcel, int length) {
……
int offset = parcel.dataPosition();
parcel.setDataPosition(MathUtils.addOrThrow(offset, length));
Parcel p = Parcel.obtain();
p.setDataPosition(0);
p.appendFrom(parcel, offset, length);
p.adoptClasscookies(parcel);
if (DEBUG) Log.d(TAG, "Retrieving " + Integer.toHexString(System.identityHashCode(this))
+ ": " + length + " bundle bytes starting at " + offset);
p.setDataPosition(0);
mParcelledData = p;
mParcelledByNative = isNativeBundle;
}
Bundle的构造方法调用了其父类baseBundle的构造方法,然后是调用了readFromParcelInner方法,readFromParcelInner方法里复制了一个新的Parcel对象然后赋予了mParcelledData变量,它保存的便是序列化后的数据。在反序列化过程时(下面会介绍内在过程)便是通过mParcelledData来还原成原来的数据。
5 Parcelable序列化过程
从上面Intent传递参数过程得知,Intent的writeToParcel方法是序列化的开始,我们来看源码:
Intent.java
public void writeToParcel(Parcel out, int flags) {
out.writeString8(mAction);
Uri.writeToParcel(out, mData);
out.writeString8(mType);
out.writeString8(mIdentifier);
out.writeInt(mFlags);
out.writeString8(mPackage);
ComponentName.writeToParcel(mComponent, out);
……
out.writeBundle(mExtras);
}
Intent里的writeToParcel方法前面会进行一些Action、Type、Flags、Package等的写入,最后一行会调用到Parcel的writeBundle方法。
Parcel.java
public final void writeBundle(@Nullable Bundle val) {
……
val.writeToParcel(this, 0);
}
Bundle.java
public void writeToParcel(Parcel parcel, int flags) {
final boolean oldAllowFds = parcel.pushAllowFds((mFlags & FLAG_ALLOW_FDS) != 0);
try {
super.writeToParcelInner(parcel, flags);
} finally {
parcel.restoreAllowFds(oldAllowFds);
}
}
Bundle的父类是baseBundle,所以再来看下baseBundle# writeToParcelInner。
baseBundle.java
void writeToParcelInner(Parcel parcel, int flags) {
……
final ArrayMap map;
synchronized (this) {
// unparcel() can race with this method and cause the parcel to recycle
// at the wrong time. So synchronize access the mParcelledData's content.
if (mParcelledData != null) {
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;
}
……
int lengthPos = parcel.dataPosition();
parcel.writeInt(-1); // dummy, will hold length
parcel.writeInt(BUNDLE_MAGIC);
int startPos = parcel.dataPosition();
// 关键代码,将数据写入parcel
parcel.writeArrayMapInternal(map);
int endPos = parcel.dataPosition();
// Backpatch length
parcel.setDataPosition(lengthPos);
int length = endPos - startPos;
parcel.writeInt(length);
parcel.setDataPosition(endPos);
}
上面我们知道,无论是Bundle.putBundle还是Bundle.putParcelable,都是将对象添加到一个ArrayMap
Parcel.java
void writeArrayMapInternal(@Nullable ArrayMapval) { …… final int N = val.size(); writeInt(N); …… int startPos; for (int i=0; i WriteArrayMapInternal方法先写入Map的长度,然后遍历Map写入每一项的key和value。着重看写value部分。
public final void writevalue(@Nullable Object v) { if (v == null) { writeInt(VAL_NULL); } else if (v instanceof String) { writeInt(VAL_STRING); writeString((String) v); } else if (v instanceof Integer) { writeInt(VAL_INTEGER); writeInt((Integer) v); } else if (v instanceof 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 XXX) { …… } else { …… } }writevalue内根据Object的类型进行不同的写入,当发现对象是Parcelable 后便再调用了writeParcelable方法。
public final void writeParcelable(@Nullable Parcelable p, int parcelableFlags) { …… writeParcelableCreator(p); p.writeToParcel(this, parcelableFlags); }Parcelable.java
public interface Parcelable { public void writeToParcel(Parcel dest, @WriteFlags int flags); }writeParcelable方法最后一行便是调用了Parcelable的writeToParcel方法,也就是我们手动实现的序列化的地方。
6 Parcelable反序列化过程
从上面Intent传递参数过程得知,在新的Activity里包含着一个新的Intent对象,而Intent内部的Bundle的mParcelledData字段便是保存了前面序列化后的数据。了解反序列化的过程,从Intent.getParcelableExtra或Bundle.getParcelable开始:
Intent.java
public @NullableT getParcelableExtra(String name) { return mExtras == null ? null : mExtras. getParcelable(name); } Bundle.java
publicT getParcelable(@Nullable String key) { unparcel(); Object o = mMap.get(key); if (o == null) { return null; } try { return (T) o; } catch (ClassCastException e) { typeWarning(key, o, "Parcelable", e); return null; } } getParcelable方法中,首先通过unparcel方法将数据还原成新的对象,然后通过key读取Map后返回对象。所以关键就是unparcel方法。
baseBundle.java
void unparcel() { synchronized (this) { final Parcel source = mParcelledData; if (source != null) { initializeFromParcelLocked(source, true, mParcelledByNative); } else { if (DEBUG) { Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this)) + ": no parcelled data"); } } } }因为前面的原因,这次mParcelledData变量已经不为空,所以接下来会调用到initializeFromParcelLocked方法。
private void initializeFromParcelLocked(@NonNull Parcel parcelledData, boolean recycleParcel, boolean parcelledByNative) { …… ArrayMapmap = mMap; if (map == null) { 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) { …… } …… } initializeFromParcelLocked方法内,判断parcelledByNative是否为true来决定使用Parcel的readArrayMapSafelyInternal方法还是readArrayMapInternal方法来进行反序列化输出ArrayMap参数。
Parcel.java
void readArrayMapSafelyInternal(@NonNull ArrayMap outVal, int N, @Nullable ClassLoader loader) { …… while (N > 0) { String key = readString(); if (DEBUG_ARRAY_MAP) Log.d(TAG, " Read safe #" + (N-1) + ": key=0x" + (key != null ? key.hashCode() : 0) + " " + key); Object value = readValue(loader); outVal.put(key, value); N--; } } void readArrayMapInternal(@NonNull ArrayMap outVal, int N, @Nullable ClassLoader loader) { …… int startPos; while (N > 0) { if (DEBUG_ARRAY_MAP) startPos = dataPosition(); String key = readString(); Object value = readValue(loader); if (DEBUG_ARRAY_MAP) Log.d(TAG, " Read #" + (N-1) + " " + (dataPosition()-startPos) + " bytes: key=0x" + Integer.toHexString((key != null ? key.hashCode() : 0)) + " " + key); outVal.append(key, value); N--; } outVal.validate(); }无论是readArrayMapSafelyInternal方法还是readArrayMapInternal方法,都是根据数据长度进行循环调用readValue方法进行读取数据。
public final Object readValue(@Nullable ClassLoader loader) { int type = readInt(); switch (type) { case VAL_NULL: return null; case VAL_STRING: return readString(); case VAL_INTEGER: return readInt(); case VAL_MAP: return readHashMap(loader); case VAL_PARCELABLE: // 关键代码,实现了Parcelable的类型的读取 return readParcelable(loader); …… }readValue方法对应序列化时的writevalue方法,会根据序列化时标明的数据的类型进行不同的读取,当发现类型是Parcelable后便再调用了readParcelable方法。
public finalT readParcelable(@Nullable ClassLoader loader) { // 关键代码1,调到readParcelableCreator方法去获取我们类中的CREATOR Parcelable.Creator> creator = readParcelableCreator(loader); if (creator == null) { return null; } if (creator instanceof Parcelable.ClassLoaderCreator>) { Parcelable.ClassLoaderCreator> classLoaderCreator = (Parcelable.ClassLoaderCreator>) creator; return (T) classLoaderCreator.createFromParcel(this, loader); } // 关键代码2,调用CREATOR里的createFromParcel方法,该方法便是返回带Parcel的构造方法,也就是反序列化的地方 return (T) creator.createFromParcel(this); } public final Parcelable.Creator> readParcelableCreator(@Nullable ClassLoader loader) { …… try { ClassLoader parcelableClassLoader = (loader == null ? getClass().getClassLoader() : loader); Class> parcelableClass = Class.forName(name, false, parcelableClassLoader); …… // 关键代码,指定字段名一定是 CREATOR Field f = parcelableClass.getField("CREATOR"); …… Class> creatorType = f.getType(); …… creator = (Parcelable.Creator>) f.get(null); } catch (IllegalAccessException e) { …… } catch (ClassNotFoundException e) { …… } catch (NoSuchFieldException e) { …… } …… return creator; } 关键代码1中,readParcelableCreator方法通过传入的ClassLoader作为参数,通过反射获取到我们在类中定义的 CREATOR变量,这里也解释了为什么实现Parcelable接口后,除了实现两个必要的接口方法外,还需要声明一定名称必须是CREATOR变量的原因。在获取了CREATOR后,便在关键代码2中,调用CREATOR里的createFromParcel方法,我们在该方法中返回带Parcel的构造方法,也就是我们手动实现反序列化的地方。
7 Parcel原理
从上面的介绍可知,Parcel对象通过obtain方法创建,序列化和反序列化的实际上最后的操作便是我们类中自己实现的writeToParcel方法和带Parcel参数的构造方法中通过一系列writeXXX 和readXXX方法来完成。下面来看看Parcel的源码情况。
7.1 Java层的Parcel类
obtain方法创建Parcel对象:
Parcel.java
public static Parcel obtain() { final Parcel[] pool = sOwnedPool; synchronized (pool) { Parcel p; for (int i=0; iobtain方法先尝试从缓存数组中去获取一个Parcel对象,若缓存中没有则传入0创建一个新的对象。
private long mNativePtr; // 用于存放 Native 层的 Parcel 对象的指针地址 private Parcel(long nativePtr) { if (DEBUG_RECYCLE) { mStack = new RuntimeException(); } init(nativePtr); } private void init(long nativePtr) { if (nativePtr != 0) { mNativePtr = nativePtr; mOwnsNativeParcelObject = false; } else { mNativePtr = nativeCreate(); mOwnsNativeParcelObject = true; } } private static native long nativeCreate();Parcel的构造函数调用了init方法进行初始化Parcel,如果传入0,则通过nativeCreate方法创建,并返回一个指针地址数值赋值给mNativePtr变量。其中,nativeCreate方法是一个Native方法。而我们进行序列化和反序列化的一系列writeXXX和readXXX方法其实也是有对应的Native方法,Parcel类只是一个代理的空壳类:
public final void writeInt(int val) { nativeWriteInt(mNativePtr, val); } public final void writeLong(long val) { nativeWriteLong(mNativePtr, val); } public final void writeFloat(float val) { nativeWriteFloat(mNativePtr, val); } public final void writeDouble(double val) { nativeWriteDouble(mNativePtr, val); } public final void writeString(@Nullable String val) { mReadWriteHelper.writeString(this, val); } public final int readInt() { return nativeReadInt(mNativePtr); } public final long readLong() { return nativeReadLong(mNativePtr); } public final float readFloat() { return nativeReadFloat(mNativePtr); } public final double readDouble() { return nativeReadDouble(mNativePtr); } public final String readString() { return mReadWriteHelper.readString(this); } public static class ReadWriteHelper { public static final ReadWriteHelper DEFAULT = new ReadWriteHelper(); public void writeString(Parcel p, String s) { nativeWriteString(p.mNativePtr, s); } public String readString(Parcel p) { return nativeReadString(p.mNativePtr); } } private static native void nativeWriteInt(long nativePtr, int val); private static native void nativeWriteLong(long nativePtr, long val); private static native void nativeWriteFloat(long nativePtr, float val); private static native void nativeWriteDouble(long nativePtr, double val); static native void nativeWriteString(long nativePtr, String val); private static native int nativeReadInt(long nativePtr); private static native long nativeReadLong(long nativePtr); private static native float nativeReadFloat(long nativePtr); private static native double nativeReadDouble(long nativePtr); static native String nativeReadString(long nativePtr);7.2 C++层的android_os_Parcel类
正常情况下,Android Studio开发时所下载的SDK源码里并不会包含Native层的代码,所以我们如果需要查阅C++层的代码需要自己另外进行下载,或者通过在线查阅,如:AndroidXRef 。回来正题,Parcel.java中所调用的Native代码的实现在android_os_Parcel.cpp中,继续查看关键方法的实现。
frameworksbasecorejniandroid_os_Parcel.cpp
static const JNINativeMethod gParcelMethods[] = { …… {"nativeCreate", "()J", (void*)android_os_Parcel_create}, …… {"nativeWriteInt", "(JI)V", (void*)android_os_Parcel_writeInt}, {"nativeWriteLong", "(JJ)V", (void*)android_os_Parcel_writeLong}, {"nativeWriteFloat", "(JF)V", (void*)android_os_Parcel_writeFloat}, {"nativeWriteDouble", "(JD)V", (void*)android_os_Parcel_writeDouble}, {"nativeWriteString", "(JLjava/lang/String;)V", (void*)android_os_Parcel_writeString}, …… {"nativeReadInt", "(J)I", (void*)android_os_Parcel_readInt}, {"nativeReadLong", "(J)J", (void*)android_os_Parcel_readLong}, {"nativeReadFloat", "(J)F", (void*)android_os_Parcel_readFloat}, {"nativeReadDouble", "(J)D", (void*)android_os_Parcel_readDouble}, {"nativeReadString", "(J)Ljava/lang/String;", (void*)android_os_Parcel_readString}, …… }; static jlong android_os_Parcel_create(JNIEnv* env, jclass clazz) { // 创建 native 层的 Parcel 对象 Parcel* parcel = new Parcel(); // 返回其指针的long值 return reinterpret_cast(parcel); } static void android_os_Parcel_writeInt(JNIEnv* env, jclass clazz, jlong nativePtr, jint val) { // nativePtr 是 android_os_Parcel_create 返回的 Parcel 对象的指针值,这里强转回 Parcel 指针 Parcel* parcel = reinterpret_cast (nativePtr); if (parcel != NULL) { // 使用Parcel指针调用其 writeInt32() 方法写入数据 const status_t err = parcel->writeInt32(val); if (err != NO_ERROR) { signalExceptionForError(env, clazz, err); } } } static void android_os_Parcel_writeString(JNIEnv* env, jclass clazz, jlong nativePtr, jstring val) { // nativePtr 是 android_os_Parcel_create 返回的 Parcel 对象的指针值,这里强转回 Parcel 指针 Parcel* parcel = reinterpret_cast (nativePtr); if (parcel != NULL) { status_t err = NO_MEMORY; if (val) { // 获取需要写入的字符串 const jchar* str = env->GetStringCritical(val, 0); if (str) { // 使用Parcel指针调用其 writeString16() 方法写入数据 err = parcel->writeString16( reinterpret_cast (str), env->GetStringLength(val)); // 释放内存 env->ReleaseStringCritical(val, str); } } else { err = parcel->writeString16(NULL, 0); } if (err != NO_ERROR) { signalExceptionForError(env, clazz, err); } } } static jint android_os_Parcel_readInt(JNIEnv* env, jclass clazz, jlong nativePtr) { // nativePtr 是 android_os_Parcel_create 返回的 Parcel 对象的指针值,这里强转回 Parcel 指针 Parcel* parcel = reinterpret_cast (nativePtr); if (parcel != NULL) { // 使用Parcel指针调用其 readInt32() 方法读取数据 return parcel->readInt32(); } return 0; } static jstring android_os_Parcel_readString(JNIEnv* env, jclass clazz, jlong nativePtr) { // nativePtr 是 android_os_Parcel_create 返回的 Parcel 对象的指针值,这里强转回 Parcel 指针 Parcel* parcel = reinterpret_cast (nativePtr); if (parcel != NULL) { size_t len; // 使用Parcel指针调用其 readString16Inplace() 方法读取数据 const char16_t* str = parcel->readString16Inplace(&len); if (str) { return env->NewString(reinterpret_cast (str), len); } return NULL; } return NULL; } 由于代码量很多,这里只列出创建native层的Parcel对象和关于Int和String两种类型的写入和读取代码,所有的类型写入和读取方法都是首先通过传入的nativePtr变量强转成Parcel指针,此变量便是android_os_Parcel_create方法创建Parcel对象返回的指针值,接着使用Parcel指针来调用相应的写入和读取操作。
frameworksnativelibsbinderParcel.cpp
status_t Parcel::writeInt32(int32_t val) { return writeAligned(val); } templatestatus_t Parcel::writeAligned(T val) { COMPILE_TIME_ASSERT_FUNCTION_SCOPE(PAD_SIZE_UNSAFE(sizeof(T)) == sizeof(T)); // 判断内存是否足够 if ((mDataPos+sizeof(val)) <= mDataCapacity) { restart_write: // 将指针移动到偏移的位置(首地址+指针的偏移量),接着写入val到内存去 *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::writeString16(const char16_t* str, size_t len) { if (str == NULL) return writeInt32(-1); // 写入字符串值长度 status_t err = writeInt32(len); if (err == NO_ERROR) { // 计算字符串所需的内存 len *= sizeof(char16_t); // 开辟缓存字符串的内存,更新内存地址的偏移量 uint8_t* data = (uint8_t*)writeInplace(len+sizeof(char16_t)); if (data) { // 将字符串复制到内存中缓存 memcpy(data, str, len); *reinterpret_cast (data+len) = 0; return NO_ERROR; } err = mError; } return err; } int32_t Parcel::readInt32() const { return readAligned (); } template status_t Parcel::readAligned(T *pArg) const { COMPILE_TIME_ASSERT_FUNCTION_SCOPE(PAD_SIZE_UNSAFE(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; } } String16 Parcel::readString16() const { size_t len; // 读取字符串 const char16_t* str = readString16Inplace(&len); if (str) return String16(str, len); return String16(); } const char16_t* Parcel::readString16Inplace(size_t* outLen) const { int32_t size = readInt32(); if (size >= 0 && size < INT32_MAX) { *outLen = size; // 读取字符串 const char16_t* str = (const char16_t*)readInplace((size+1)*sizeof(char16_t)); if (str != NULL) { return str; } } *outLen = 0; return NULL; } 所以序列化和反序列化最终的写入和读取是在native层的Parcel.cpp中完成,在Parcel.cpp里会进行开辟一块连续的内存用来进行数据的缓存,其中内存的首地址是mData,偏移量是mDataPos。
当写入一个数据时,首先会将指针移动到对应的位置,即mData + mDataPos,然后再将数据写入内存,最后会重新计算mDataPos 的偏移量。到下次要写入数据时同理会将数据写入到内存的后面。
也是因为开辟的是一块连续的内存,所以在读取数据时只要保证跟写入数据的顺序一致,便能将内存中的数据读出。
8 总结
- 从使用上对比 Serializable 和 Parcelable,Serializable 会显得简单一些,两者都是实现不同的接口,但是 Serializable 可全自动序列化和反序列化,Parcelable 需要手动去 write 和 read 一系列方法才能完成序列化和反序列化。
- 从场景来考虑,Serializable 支持数据持久化存储到本地磁盘、网络传输等,Parcelable 则不支持。如果只在组件间或者跨进程组件间的传输数据的场景,虽然两者都是支持的。但是在内存使用和性能上 Parcelable 会优出 Serializable 很多,因为 Serializable 在序列化和反序列化过程中都需要较多的反射和IO操作,Parcelable 设计的目的就是为了提高这种场景下数据传输性能,因为其内部是通过Parcel来实现,Parcel 是一种可通过 IBinder 高性能传输数据和对象的容器,内部采用共享内存的方式实现用户空间和内核空间的数据交换,本质是 Native 层的共享内存。
- Parcelable的使用需要类继承Parcelable接口,并且必须重写describeContents 和 writeToParcel 两个方法和存在一个带Parcel类型参数的构造方法,以及必须声明一个非空的、名字必须是CREATOR的、类型是Parcelable.Creator
的静态变量。 - writeToParcel方法是实现序列化写操作的地方,而带Parcel参数的构造方法是实现反序列化读操作的地方。Parcel对象的写入和读取的顺序必须保持一致,否则会发生数值错误或者类型异常崩溃情况。
- 从IPC过程可知,当Client端发起跨进程请求时前,先调用Intent的writeToParcel方法,将Intent内存中的ArrayMap输出一个android.os.Parcel 类型的_data变量进行Parcelable的序列化。
- 从IPC过程可知,远程请求会通过系统底层封装后交由onTransact方法来处理,些时会接收android.os.Parcel 类型的_data变量,再创建新的Intent对象,Intent的构造函数会接收这个_data变量,并使用mParcelledData变量进行存储起来,当调用到Intent的getParcelableExtra或者getParcelable方法获取数据时,便是对内存中mParcelledData进行反序列化成ArrayMap。



