Bundle、Parcel解析

Bundle、Parcel解析

Bundle 被我们经常用来进行启动Activity、service以及传递数据.Bundle支持的数据类型有原始类型以及其封装类,Parcelable 类型、Size类型、SizeF类型以及Serializable类型。

Bundle基本使用

Intent添加数据

我们先看一下Bundle的添加传递信息的方法,
添加Parcelable类型参数。

1
2
3
4
5
public void putParcelable(@Nullable String key, @Nullable Parcelable value) {
unparcel();
mMap.put(key, value);
mFdsKnown = false;
}

添加float类型参数。

1
2
3
4
5
6
7
8
9
@Override
public void putFloat(@Nullable String key, float value) {
super.putFloat(key, value);
}
//调用父类方法保存参数
void putFloat(@Nullable String key, float value) {
unparcel();
mMap.put(key, value);
}

通过putParcelable 方法和putFloat 方法可以看出最后数据都会保存在mMap对象中。

1
2
3
4
ArrayMap<String, Object> mMap = null;
.....
mMap = capacity > 0 ?
new ArrayMap<String, Object>(capacity) : new ArrayMap<String, Object>();

mMap的定义可以看出mMap就是一个ArrayMap,存放键值对,而且value的类型是Object类型,就是所有参数的类型都被转换为Object类型保存在mMap中。而系统如何使用这些数据以下再说。

Bundle数据储存

找到Intent数据保存的切入点

通过研究activity的startActivity方法,发现最后会调用ActivityManagerProxy的startActivity方法,该类是ActivityManagerNative的内部类.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
参数重点是intent,就是我放入startActivity的intent
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);
//使用该方法把Intent的Bundle的参数导入Parcel的data中,
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方法,把Bundle中的数据导入进Parcel类型数据中,再看writeToParcel的方法:

1
2
3
4
5
6
7
public void writeToParcel(Parcel out, int flags) {
....
省略
....
//mExtras就是保持的Bundle类型数据,也就是我们要保存的数据
out.writeBundle(mExtras);
}

顺着代码调用进入到了Parcel的writeBundle方法,

1
2
3
4
5
6
7
8
public final void writeBundle(Bundle val) {
if (val == null) {
writeInt(-1);
return;
}
//调用需要传递数据的Bundle的writeToParcel方法
val.writeToParcel(this, 0);
}

最后还是回到Bundle类本身。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
//Bundle的方法
public void writeToParcel(Parcel parcel, int flags) {
final boolean oldAllowFds = parcel.pushAllowFds(mAllowFds);
try {
//调用父类的方法写入数据
super.writeToParcelInner(parcel, flags);
} finally {
parcel.restoreAllowFds(oldAllowFds);
}
}
//BaseBundle把本身数据写入parcel中
void writeToParcelInner(Parcel parcel, int flags) {
if (mParcelledData != null) {
if (mParcelledData == EMPTY_PARCEL) {
parcel.writeInt(0);
} else {
int length = mParcelledData.dataSize();
parcel.writeInt(length);
parcel.writeInt(BUNDLE_MAGIC);
parcel.appendFrom(mParcelledData, 0, length);
}
} else {
// Special case for empty bundles.
if (mMap == null || mMap.size() <= 0) {
parcel.writeInt(0);
return;
}
int lengthPos = parcel.dataPosition();
parcel.writeInt(-1); // dummy, will hold length
parcel.writeInt(BUNDLE_MAGIC);
int startPos = parcel.dataPosition();
//核心代码,mMap就是上面一开始保存数据的对象,也就是所有需要传输的数据都在该对象中。又返回到了Parcel的方法中
parcel.writeArrayMapInternal(mMap);
int endPos = parcel.dataPosition();
// Backpatch length
parcel.setDataPosition(lengthPos);
int length = endPos - startPos;
parcel.writeInt(length);
parcel.setDataPosition(endPos);
}
}

Parcel最后都都调用write方法保存,最后都会调用本地方法,因为Parcel的数据结构都在c层,所以不做深入研究。有兴趣的可以看看,路径为frameworks\base\core\jni\android_os_Parcel.cpp,以及frameworks\native\libs\binder\Parcel.cpp。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void writeArrayMapInternal(ArrayMap<String, Object> val) {
....
省略
....
int startPos;
for (int i=0; i<N; i++) {
if (DEBUG_ARRAY_MAP) startPos = dataPosition();
// 写入mMap的key值
writeString(val.keyAt(i));
// 写入mMap的value值
writeValue(val.valueAt(i));
if (DEBUG_ARRAY_MAP) Log.d(TAG, " Write #" + i + " "
+ (dataPosition()-startPos) + " bytes: key=0x"
+ Integer.toHexString(val.keyAt(i) != null ? val.keyAt(i).hashCode() : 0)
+ " " + val.keyAt(i));
}
}

走到这里,基本可以理清mMap的数据最后都会写入Parcel中,也就是说Bundle也是使用Parcel传递的数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
public final void writeValue(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 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);
} else if (v instanceof Float) {
writeInt(VAL_FLOAT);
writeFloat((Float) v);
} else if (v instanceof Double) {
writeInt(VAL_DOUBLE);
writeDouble((Double) v);
} else if (v instanceof Boolean) {
writeInt(VAL_BOOLEAN);
writeInt((Boolean) v ? 1 : 0);
} else if (v instanceof CharSequence) {
// Must be after String
writeInt(VAL_CHARSEQUENCE);
writeCharSequence((CharSequence) v);
} else if (v instanceof List) {
writeInt(VAL_LIST);
writeList((List) v);
} else if (v instanceof SparseArray) {
writeInt(VAL_SPARSEARRAY);
writeSparseArray((SparseArray) v);
} else if (v instanceof boolean[]) {
writeInt(VAL_BOOLEANARRAY);
writeBooleanArray((boolean[]) v);
} else if (v instanceof byte[]) {
writeInt(VAL_BYTEARRAY);
writeByteArray((byte[]) v);
} else if (v instanceof String[]) {
writeInt(VAL_STRINGARRAY);
writeStringArray((String[]) v);
} else if (v instanceof CharSequence[]) {
// Must be after String[] and before Object[]
writeInt(VAL_CHARSEQUENCEARRAY);
writeCharSequenceArray((CharSequence[]) v);
} else if (v instanceof IBinder) {
writeInt(VAL_IBINDER);
writeStrongBinder((IBinder) v);
} else if (v instanceof Parcelable[]) {
writeInt(VAL_PARCELABLEARRAY);
writeParcelableArray((Parcelable[]) v, 0);
} else if (v instanceof int[]) {
writeInt(VAL_INTARRAY);
writeIntArray((int[]) v);
} else if (v instanceof long[]) {
writeInt(VAL_LONGARRAY);
writeLongArray((long[]) v);
} else if (v instanceof Byte) {
writeInt(VAL_BYTE);
writeInt((Byte) v);
} else if (v instanceof PersistableBundle) {
writeInt(VAL_PERSISTABLEBUNDLE);
writePersistableBundle((PersistableBundle) v);
} else if (v instanceof Size) {
writeInt(VAL_SIZE);
writeSize((Size) v);
} else if (v instanceof SizeF) {
writeInt(VAL_SIZEF);
writeSizeF((SizeF) v);
} else {
Class<?> clazz = v.getClass();
if (clazz.isArray() && clazz.getComponentType() == Object.class) {
// Only pure Object[] are written here, Other arrays of non-primitive types are
// handled by serialization as this does not record the component type.
writeInt(VAL_OBJECTARRAY);
writeArray((Object[]) v);
} else if (v instanceof Serializable) {
// Must be last
writeInt(VAL_SERIALIZABLE);
writeSerializable((Serializable) v);
} else {
throw new RuntimeException("Parcel: unable to marshal value " + v);
}
}
}

要写入Parcel的对象要先判断类型,然后先写入该对象的类型标志,再调用对应类型的方法写入要保存数据。这里需要关注的只有一点,就是Map类型的对象保存,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public final void writeMap(Map val) {
writeMapInternal((Map<String, Object>) val);
}
void writeMapInternal(Map<String,Object> val) {
if (val == null) {
writeInt(-1);
return;
}
Set<Map.Entry<String,Object>> entries = val.entrySet();
writeInt(entries.size());
for (Map.Entry<String,Object> e : entries) {
writeValue(e.getKey());
writeValue(e.getValue());
}
}

Parcel只是获取Map的键值对,然后保存,该方法没有保存Map类的信息,也就是类型擦除,最后经过startActivity跳到其他Activity时,再获取时所以Map类型对象都会转换为HashMap对象,所以Bundle传递数据不能使用其他Map类型,比如TreeMap。

Parcel转换为类型对象

这里还是从Intent类看起,Intent实现了Parcelable,ActivityManagerProxy的startActivity方法中intent.writeToParcel(data, 0);通过这个方法Intent把对象的具体信息写入Parcel中,通过一连串的传输最后又通过序列化转为Intent对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
protected Intent(Parcel in) {
readFromParcel(in);
}
public void readFromParcel(Parcel in) {
//恢复action属性
setAction(in.readString());
//恢复Data属性
mData = Uri.CREATOR.createFromParcel(in);
//Type属性
mType = in.readString();
//Flag属性,启动模式
mFlags = in.readInt();
mPackage = in.readString();
mComponent = ComponentName.readFromParcel(in);
if (in.readInt() != 0) {
mSourceBounds = Rect.CREATOR.createFromParcel(in);
}
int N = in.readInt();
if (N > 0) {
mCategories = new ArraySet<String>();
int i;
for (i=0; i<N; i++) {
mCategories.add(in.readString().intern());
}
} else {
mCategories = null;
}
if (in.readInt() != 0) {
mSelector = new Intent(in);
}
if (in.readInt() != 0) {
mClipData = new ClipData(in);
}
mContentUserHint = in.readInt();
//传递的数据
mExtras = in.readBundle();
}

使用过Parcelable的都知道反序列化最后都会调用包含Parcel参数的构造函数,对于Intent就是Intent(Parcel in),然后调用readFromParcel方法,把Intent的相关属性恢复。对于Bundle数据的恢复是调用了Parcel的readBundle方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
//Parcel.java
public final Bundle readBundle() {
return readBundle(null);
}
//Parcel.java
public final Bundle readBundle(ClassLoader loader) {
//获取Bundle保存数据的长度
int length = readInt();
if (length < 0) {
if (Bundle.DEBUG) Log.d(TAG, "null bundle: length=" + length);
return null;
}
final Bundle bundle = new Bundle(this, length);
if (loader != null) {
bundle.setClassLoader(loader);
}
return bundle;
}
//Bundle.java
Bundle(Parcel parcelledData, int length) {
super(parcelledData, length);
mHasFds = mParcelledData.hasFileDescriptors();
mFdsKnown = true;
}
//BaseBundle.java
BaseBundle(Parcel parcelledData, int length) {
readFromParcelInner(parcelledData, length);
}
//BaseBundle.java
private void readFromParcelInner(Parcel parcel, int length) {
if (length == 0) {
// Empty Bundle or end of data.
mParcelledData = EMPTY_PARCEL;
return;
}
//获取数据类型魔数,若不为BUNDLE_MAGIC,则抛出异常。
int magic = parcel.readInt();
if (magic != BUNDLE_MAGIC) {
//noinspection ThrowableInstanceNeverThrown
throw new IllegalStateException("Bad magic number for Bundle: 0x"
+ Integer.toHexString(magic));
}
// Advance within this Parcel
int offset = parcel.dataPosition();
parcel.setDataPosition(offset + length);
Parcel p = Parcel.obtain();
p.setDataPosition(0);
//通过传入的Parcel对象生成一个新的Parcel对象,也就是截取Parcel的数据组成新的Parcel对象。
p.appendFrom(parcel, offset, length);
if (DEBUG) Log.d(TAG, "Retrieving " + Integer.toHexString(System.identityHashCode(this))
+ ": " + length + " bundle bytes starting at " + offset);
p.setDataPosition(0);
//然后把新生成的Parcel赋值给Bundle。
mParcelledData = p;
}

Intent的readBundle方法没有直接的把Parcel数据解析为对象数据,还是以Parcel形式保存着,那Parcel是什么时候解析的呢?然后我查看了一下Bundle的get方法,发现最后都调用了unparcel()。

1
2
3
4
5
6
7
8
9
@Override
public byte getByte(String key) {
return super.getByte(key);
}
byte getByte(String key) {
unparcel();
return getByte(key, (byte) 0);
}

然后就开始研究unparcel()方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
synchronized void unparcel() {
//没有Parcel数据,不创建mMap对象,直接返回
if (mParcelledData == null) {
if (DEBUG) Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this))
+ ": no parcelled data");
return;
}
//mParcelledData为默认的EMPTY_PARCEL,创建或清理mMap对象,不解析数据,直接返回
if (mParcelledData == EMPTY_PARCEL) {
if (DEBUG) Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this))
+ ": empty");
if (mMap == null) {
mMap = new ArrayMap<String, Object>(1);
} else {
mMap.erase();
}
mParcelledData = null;
return;
}
//解析Parcel数据,或取Parcel含有多少键值对数据
int N = mParcelledData.readInt();
if (DEBUG) Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this))
+ ": reading " + N + " maps");
if (N < 0) {
return;
}
if (mMap == null) {
mMap = new ArrayMap<String, Object>(N);
} else {
mMap.erase();
mMap.ensureCapacity(N);
}
//核心功能读取数据放入mMap中
mParcelledData.readArrayMapInternal(mMap, N, mClassLoader);
mParcelledData.recycle();
mParcelledData = null;
if (DEBUG) Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this))
+ " final map: " + mMap);
}

unparcel方法的就是判断当前的Bundle是否含有Parcel数据,然后根据结果创建mMap对象,然后调用readArrayMapInternal把Parcel中的数据发序列化为对象。然后存放在mMap中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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;
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();
}

这里我们可以看到先读取了key,然后调用readValue获取value的值,然后存储到mMap中。我在这里主要分析一下Map类型数据的获取,可以很明了的知道为什么强制为TreeMap会报错。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public final Object readValue(ClassLoader loader) {
int type = readInt();
switch (type) {
case VAL_NULL:
return null;
...
省略
...
case VAL_MAP:
return readHashMap(loader);
...
省略
...
}
}
public final HashMap readHashMap(ClassLoader loader)
{
int N = readInt();
if (N < 0) {
return null;
}
HashMap m = new HashMap(N);
readMapInternal(m, N, loader);
return m;
}

一切都很明了,Parcel对Map类型的数据进行反序列化时,获取key\value的值都存放在HashMap中,然后返回HashMap,也就是说所有Map类型数据最后都会转为HashMap类型数据。强制当然会报错。