在 Android 四大组件中 Activity、Service、Receiver 都支持在 Intent 中附加传递 Bundle 数据。
Bundle 是以键值对的形式存储数据,支持基本数据类型、数组、实现 Serializable 或 Parcelable 接口的对象以及一些 Android 支持的特殊对象。
// 发送数据
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
Bundle bundle = new Bundle();
bundle.putString("userName", "wshunli");
intent.putExtras(bundle);
startActivity(intent);
// 接收数据
Bundle bundle = getIntent().getExtras();
String userName = null;
if (bundle != null) {
userName = bundle.getString("userName");
}
Log.d(TAG, "userName: " + userName);
使用 Bundle 的 put 与 get 方法族将数据保存至 bundle 对象,调用 Intent 的 putExtras 方法即可。
Bundle 源码分析,Bundle 继承自 BaseBundle 并实现了 Cloneable 和 Parcelable 接口。
以 Boolean 类型数据存取为例:
ArrayMap<String, Object> mMap = null;
public void putBoolean(@Nullable String key, boolean value) {
unparcel();
mMap.put(key, value);
}
public boolean getBoolean(String key, boolean defaultValue) {
unparcel();
Object o = mMap.get(key);
if (o == null) {
return defaultValue;
}
try {
return (Boolean) o;
} catch (ClassCastException e) {
typeWarning(key, o, "Boolean", defaultValue, e);
return defaultValue;
}
}
根据源码可知数据存取都是在 ArrayMap 对象中。
其中 unparcel() 方法源码如下:
/*
* 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;
/* package */ void unparcel() {
synchronized (this) {
final Parcel source = mParcelledData;
if (source != null) {
initializeFromParcelLocked(source, /*recycleParcel=*/ true);
} else {
if (DEBUG) {
Log.d(TAG, "unparcel "
+ Integer.toHexString(System.identityHashCode(this))
+ ": no parcelled data");
}
}
}
}
private void initializeFromParcelLocked(@NonNull Parcel parcelledData, boolean recycleParcel) {
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(parcelledData)) {
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;
}
final int count = parcelledData.readInt();
if (DEBUG) {
Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this))
+ ": reading " + count + " maps");
}
if (count < 0) {
return;
}
ArrayMap<String, Object> map = mMap;
if (map == null) {
map = new ArrayMap<>(count);
} else {
map.erase();
map.ensureCapacity(count);
}
try {
parcelledData.readArrayMapInternal(map, count, 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;
if (recycleParcel) {
recycleParcel(parcelledData);
}
mParcelledData = null;
}
if (DEBUG) {
Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this))
+ " final map: " + mMap);
}
}
只有使用 Bundle 构造函数实例化 Bundle 对象时,才会给 mParcelledData 赋值,其他大部分情况下 mParcelledData 都是 null 。
BaseBundle(Parcel parcelledData) {
readFromParcelInner(parcelledData);
}
void readFromParcelInner(Parcel parcel) {
// Keep implementation in sync with readFromParcel() in
// frameworks/native/libs/binder/PersistableBundle.cpp.
int length = parcel.readInt();
readFromParcelInner(parcel, length);
}
private void readFromParcelInner(Parcel parcel, int length) {
if (length < 0) {
throw new RuntimeException("Bad length in parcel: " + length);
} else if (length == 0) {
// Empty Bundle or end of data.
mParcelledData = NoImagePreloadHolder.EMPTY_PARCEL;
return;
}
final int magic = parcel.readInt();
if (magic != BUNDLE_MAGIC) {
throw new IllegalStateException("Bad magic number for Bundle: 0x"
+ Integer.toHexString(magic));
}
if (parcel.hasReadWriteHelper()) {
// If the parcel has a read-write helper, then we can't lazily-unparcel it, so just
// unparcel right away.
synchronized (this) {
initializeFromParcelLocked(parcel, /*recycleParcel=*/ false);
}
return;
}
// Advance within this Parcel
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;
}
从上述代码片段可以知道 mParcelledData 的取值有3种情况:
mParcelledData = null
mParcelledData = NoImagePreloadHolder.EMPTY_PARCEL
mParcelledData = Parcel.obtain()
在 unparcel() 方法中分别对这三种情况进行了处理,如果为 null 不进行任何操作返回;如果为 EMPTY_PARCEL 则使用 mMap = new ArrayMap<>(1)
创建长度为 1 的 ArrayMap 对象;如果为 Parcel.obtain() 则实例化 ArrayMap 对象并存储数据。
参考资料
1、Android Bundle详解 - CSDN博客
https://blog.csdn.net/cswhale/article/details/39053411
2、Android Bundle总结 - CSDN博客
https://blog.csdn.net/ylyg050518/article/details/72638852
3、Android细节问题 —— 有了Intent,为什么还要有Bundle? - 简书
https://www.jianshu.com/p/e9db0797293b
评论 (0)