Bundle的理解和使用
1、什么是Bundle
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是用来传递数据的,我们暂将Bundle理解为Android中用来传递数据的一个容器。官方文档对Bundle的说明如下:
Bundle实现了Parcelable接口,所以他可以方便的在不同进程间传输,这里要注意我们传输的数据必须能够被序列化;
先看官方文档对其的描述:A mapping from String values to various Parcelable types.
可以看出,它和Map类型有异曲同工之妙,同时实现了Parcelable接口,那么显然,它是支持进程间通讯的。所以,Bundle可以看做是一个特殊的Map类型,它支持进程间通讯,保存了特定的数据。
我们经常使用Bundle在Activity之间传递数据,传递的数据可以是boolean、byte、int、long、float、double、string等基本类型或它们对应的数组,也可以是对象或对象数组。当Bundle传递的是对象或对象数组时,必须实现Serializable 或Parcelable接口。下面分别介绍Activity之间如何传递基本类型、传递对象。
以下是Bundle的几个常用方法:
①putXxx(String key,Xxx value):Xxx表示一系列的数据类型,比如String,int,float,Parcelable,Serializable等类型,
以键-值对形式保存数据。
②getXxx(String key):根据key值获取Bundle中的数据。
2、Bundle源码分析
首先看它的声明
public final class Bundle extends BaseBundle implements Cloneable, Parcelable
第一,它使用final修饰,所以不可以被继承
第二,它实现了两个接口,cloneable和Parcelable,这就意味着他必须实现以下方法:
在public Object clone()
public int describeContents()
public void writeToParcel(Parcel parcel, int flags)
public void readFromParcel(Parcel parcel)
public static final Parcelable.Creator<Bundle> CREATOR = new Parcelable.Creator<Bundle>()
再看他的内存结构:
ArrayMap<String, Object> mMap = null;
使用的是ArrayMap,这个集合类存储的也是键值对,但是与Hashmap不同的是,hashmap采用的是“数组+链表”的方式存储,而Arraymap中使用的是两个数组进行存储,一个数组存储key,一个数组存储value,内部的增删改查都将会使用二分查找来进行,这个和SparseArray差不多,只不过sparseArray的key值只能是int型的,而Arraymap可以是map型,所以在数据量不大的情况下可以使用这两个集合代替hashmap去优化性能;
我们知道Bundle其实就是一个容器,内部使用了Arraymap去存储数据,那么就必然会提供get,put方法,由于Bundle支持的数据类型太多,这里我们就看一个布尔类型的,其他类型的方式都差不多;
2.1、举例说明:
getBoolean
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;
}
}
数据读取的逻辑很简单,就是通过key从ArrayMap里读出保存的数据,并转换成对应的类型返回,当没找到数据或发生类型转换异常时返回缺省值。
putBoolean
public void putBoolean(@Nullable String key, boolean value) {
unparcel();
mMap.put(key, value);
}
这里出现了一个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);
}
}
先来看下BaseBundle中mParcelledData的定义:
Parcel mParcelledData = null;
在大部分情况下mParcelledData都是null,因此unparcel()直接返回。当使用构造函数public Bundle(Bundle b)创建Bundle时,会给mParcelledData赋值;
oid copyInternal(BaseBundle from, boolean deep) {
synchronized (from) {
if (from.mParcelledData != null) {
if (from.isEmptyParcel()) {
mParcelledData = NoImagePreloadHolder.EMPTY_PARCEL;
} else {
mParcelledData = Parcel.obtain();
mParcelledData.appendFrom(from.mParcelledData, 0,
from.mParcelledData.dataSize());
mParcelledData.setDataPosition(0);
}
} else {
mParcelledData = null;
}
if (from.mMap != null) {
if (!deep) {
mMap = new ArrayMap<>(from.mMap);
} else {
final ArrayMap<String, Object> fromMap = from.mMap;
final int N = fromMap.size();
mMap = new ArrayMap<>(N);
for (int i = 0; i < N; i++) {
mMap.append(fromMap.keyAt(i), deepCopyValue(fromMap.valueAt(i)));
}
}
} else {
mMap = null;
}
mClassLoader = from.mClassLoader;
}
}
从上述代码片段可以知道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;
3、使用说明
Bundle提供了各种常用类型的putXxx()/getXxx()方法,用于读写基本类型的数据。Bundle操作基本数据类型的API表格如下所示:
基本类型对象
Bundle bundle = new Bundle();
Intent intent=new Intent(MainActivity.this,Main2Activity.class);
//设置数据
String name="zhangSan";
String num="88888";
//把数据保存到Bundle里
bundle.putString("name", name);
bundle.putString("num",num);
//把bundle放入intent里
intent.putExtra("Message",bundle);
startActivity(intent);
在用Bundle传递出去数据之后,那如何解析出来呢?
Intent intent=getIntent();
// 实例化一个Bundle
Bundle bundle=intent.getExtras();
String name=bundle.getString("name");
String num=bundle.getString("num");
传递Serializable类型的对象
Serializable是一个对象序列化的接口。一个类只有实现了Serializable接口,它的对象才是可序列化的。因此如果要序列化某些类的对象,这些类就必须实现Serializable接口。而实际上,Serializable是一个空接口,没有什么具体内容,它的目的只是简单的标识一个类的对象可以被序列化。
People people=new people();//People类
//设置数据
String Name="zhangSan";
String Num="88888";
people.setName(Name);
people.setNum(Num);
// 实例化一个Bundle
Bundle bundle = new Bundle();
// 把people数据放入到bundle中
bundle.putSerializable("people",people);
Intent intent=new Intent(MainActivity.this,Main2Activity.class);
intent.putExtras(bundle);
startActivity(intent);
如何解析出来呢?
Intent intent=getIntent();
// 实例化一个Bundle
Bundle bundle=intent.getExtras();
//获取里面的people里面的数据
People people= (people) bundle.getSerializable("people");
String name=people.getName();
String num=people.getNum();
参考
1、https://blog.csdn.net/pgg_cold/article/details/79468702
2、https://blog.csdn.net/a553181867/article/details/51166600
3、https://blog.csdn.net/cswhale/article/details/39053411