Bundle的使用场景
Bundle对于Android开发者来说肯定非常眼熟,它经常出现在以下场合:
- Activity状态数据的保存与恢复涉及到的两个回调:void onSaveInstanceState (Bundle
outState)、void onCreate (Bundle savedInstanceState) - Fragment的setArguments方法:void setArguments (Bundle args)
- 消息机制中的Message的setData方法:void setData (Bundle data)
…
Bundle从字面上解释为“一捆、一批、一包”,可以将Bundle理解为Android中用来传递数据的一个容器。
官方文档对Bundle的说明如下:
Bundle实现了Parcelable接口,所以他可以方便的在不同进程间传输,这里要注意我们传输的数据必须能够被序列化;
Bundle源码分析
Bundle的继承关系:
/**
* A mapping from String keys to various {@link Parcelable} values.
*
* @see PersistableBundle
*/
public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
第一,Bundle使用final修饰,所以不可以被继承
第二,Bundle实现了两个接口,cloneable和Parcelable,这就意味着他必须实现以下方法:
public Object clone()
public int describeContents()
public void writeToParcel(Parcel parcel, int flags)
public static final Parcelable.Creator<Bundle> CREATOR = new Parcelable.Creator<Bundle>()
Bundle有两个重要成员变量:mMap 和 mParcelledData
// Invariant - exactly one of mMap / mParcelledData will be null
// (except inside a call to unparcel)
ArrayMap<String, Object> mMap = null;
/*
* If mParcelledData is non-null, then mMap will be null and the
* data are stored as a Parcel containing a Bundle. When the data
* are unparcelled, mParcelledData willbe set to null.
*/
Parcel mParcelledData = null;
可以看到,Bundle其实就是一个容器,内部使用了Arraymap去存储数据。
当Bundle里的数据还没有被打包(parcel)或者已经被unparcel时,数据存储在mMap中,即mMap非null,而mParcelledData = null。
当Bundle里的数据已经被打包时,数据存储在mParcelledData 中,而mMap = null。
Parcelable
Bundle实现了Parcelable接口,意味着Bundle对象可以转为Parcel对象。
ArrayMap
ArrayMap是Android特有的容器,它设计上更多的是考虑内存的优化,在数据量比较小时性能比较好,用于取代HashMap。
Parcel
Parcel 和 Parcelable(Parcelable接口用于将任意实现了Parcelable接口的对象转为Parcel对象) 是Android专门针对高性能IPC传输而设计的,并不是为了通用的序列化方案而设计的。因此,Parcel数据不能进行持久化,因为Parcel 里面的数据的实现方式可能会改变,这会导致旧的Parcel数据无法被读取。
在Android系统中,定位为针对内存受限的设备,因此对性能要求更高,另外系统中采用了新的IPC(进程间通信)机制,必然要求使用性能更出色的对象传输方式。因此,Parcel被设计出来,其定位就是用于IPC的轻量级的高效的对象序列化和反序列化机制。
Android中序列化有以下几个特征:
1. 整个读写全是在内存中进行。
2. 读写时是4字节对齐的
3. 如果预分配的空间不够时,会一次多分配50%;
4. 对于普通数据,使用的是mData内存地址,对于IBinder类型的数据以及FileDescriptor使用的是mObjects内存地址。后者是通过flatten_binder()和unflatten_binder()实现的,目的是反序列化时读出的对象就是原对象而不用重新new一个新对象。
writeToParcel方法
将Bundle对象里的数据写成一个Parcel对象。
CREATOR
CREATOR对象是实现了Parcelable.Creator接口的一个对象,用于从Parcel对象中生成Parcelable的实例(这里就是用于生成Bundle对象)。
unparcel()方法
/* package */ void unparcel() {
synchronized (this) {
final Parcel parcelledData = mParcelledData;
if (parcelledData == null) {
if (DEBUG) Log.d(TAG, "unparcel "
+ Integer.toHexString(System.identityHashCode(this))
+ ": no parcelled data");
return;
}
if (LOG_DEFUSABLE && sShouldDefuse && (mFlags & FLAG_DEFUSABLE) == 0) {
Slog.wtf(TAG, "Attempting to unparcel a Bundle while in transit; this may "
+ "clobber all data inside!", new Throwable());
}
if (isEmptyParcel()) {
if (DEBUG) Log.d(TAG, "unparcel "
+ Integer.toHexString(System.identityHashCode(this)) + ": empty");
if (mMap == null) {
mMap = new ArrayMap<>(1);
} else {
mMap.erase();
}
mParcelledData = null;
return;
}
int N = parcelledData.readInt();
if (DEBUG) Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this))
+ ": reading " + N + " maps");
if (N < 0) {
return;
}
ArrayMap<String, Object> map = mMap;
if (map == null) {
map = new ArrayMap<>(N);
} else {
map.erase();
map.ensureCapacity(N);
}
try {
parcelledData.readArrayMapInternal(map, N, mClassLoader);
} catch (BadParcelableException e) {
if (sShouldDefuse) {
Log.w(TAG, "Failed to parse Bundle, but defusing quietly", e);
map.erase();
} else {
throw e;
}
} finally {
mMap = map;
parcelledData.recycle();
mParcelledData = null;
}
if (DEBUG) Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this))
+ " final map: " + mMap);
}
}
mParcelledData的取值有3种情况:
- mParcelledData = EMPTY_PARCEL
- mParcelledData = Parcel.obtain()
- mParcelledData = null
在unparcel()方法中就对上述几种情况做了不同的处理,当mParcelledData为null时,直接返回;当mParcelledData为EMPTY_PARCEL时,会创建一个容量为1的ArrayMap对象;当mParcelledData为Parcel.obtain()时,则会将里面的数据读出,并创建一个ArrayMap,并将数据存储到ArrayMap对象里面,同时将mParcelledData回收并置为null;
参考:
Android面试题(25)-Bundle机制Android中的Parcel机制 实现Bundle传递对象Android Bundle总结
Android跨进程通信IPC之6——Parcel–Binder对象的写入和读出