前言
Bundle对于android开发者来说肯定非常熟悉,它经常出现在以下场合:
- Activity状态数据的保存与恢复涉及到的两个回调:void onSaveInstanceState (Bundle outState)、void onCreate (Bundle savedInstanceState)
- Fragment的setArguments方法:void setArguments(Bundle args)
- 消息机制中的Message的setData方法:void setData(Bundle data)
Bundle是用来传递数据的,可以将其理解为android中用来传递数据的一个容器,官方意为Bundle封装了String值到各种Parcelable类型数据的映射。
Bundle源码分析
Bundle位于android.os包中,是一个final类,这就注定了Bundle不能被继承。Bundle继承自BaseBundle并实现了Cloneable和Parcelable两个接口。
由于实现了Cloneable和Parcelable接口,因此以下几个重载是必不可少的:
|
|
Bundle的几个公有构造方法
公有构造方法 | 说明 |
---|---|
public Bundle() | Constructs a new, empty Bundle |
public Bundle(ClassLoader loader) | Constructs a new, empty Bundle that uses a specific ClassLoader for instantiating Parcelable and Serializable objects. |
public Bundle(int capacity) | Constructs a new, empty Bundle sized to hold the given number of elements. |
public Bundle(Bundle b) | Constructs a Bundle containing a copy of the mappings from the given Bundle. |
public Bundle(PersistableBundle b) | Constructs a Bundle containing a copy of the mappings from the given PersistableBundle. |
Bundle的put与get方法族
Bundle的功能是用来保存数据,那么必然提供了一系列存取数据的方法,这些方法太多了,几乎能够存取任何类型的数据,具体整理为下表:
相关保存方法 | 相关读取方法 |
---|---|
public void putBoolean(String key, boolean value) | public boolean getBoolean(String key) |
public void putByte(String key, byte value) | public byte getByte(String key) |
public void putChar(String key, char value) | public char getChar(String key) |
public void putShort(String key, short value) | public short getShort(String key) |
public void putFloat(String key, float value) | public float getFloat(String key) |
public void putCharSequence(String key, CharSequence value) | public CharSequence getCharSequence(String key) |
public void putParcelable(String key, Parcelable value) | public T getParcelable(String key) |
public void putSize(String key, Size value) | public Size getSize(String key) |
public void putSizeF(String key, SizeF value) | public SizeF getSizeF(String key) |
public void putParcelableArray(String key, Parcelable[] value) | public Parcelable[] getParcelableArray(String key) |
public void putParcelableArrayList(String key, ArrayList value) | public ArrayList getParcelableArrayList(String key) |
public void putSparseParcelableArray(String key, SparseArray value) | public SparseArray getSparseParcelableArray(String key) |
public void putIntegerArrayList(String key, ArrayList value) | public ArrayList getIntegerArrayList(String key) |
public void putStringArrayList(String key, ArrayList value) | public ArrayList getStringArrayList(String key) |
public void putCharSequenceArrayList(String key, ArrayList value) | public ArrayList getCharSequenceArrayList(String key) |
public void putSerializable(String key, Serializable value) | public Serializable getSerializable(String key) |
public void putBooleanArray(String key, boolean[] value) | public boolean[] getBooleanArray(String key) |
public void putByteArray(String key, byte[] value) | public byte[] getByteArray(String key) |
public void putShortArray(String key, short[] value) | public short[] getShortArray(String key) |
public void putCharArray(String key, char[] value) | public char[] getCharArray(String key) |
public void putFloatArray(String key, float[] value) | public float[] getFloatArray(String key) |
public void putCharSequenceArray(String key, CharSequence[] value) | public CharSequence[] getCharSequenceArray(String key) |
public void putBundle(String key, Bundle value) | public Bundle getBundle(String key) |
public void putBinder(String key, IBinder value) | public IBinder getBinder(String key) |
除了上述存取数据涉及的方法外,Bundle还提供了一个clear方法:public void clear(),该方法可用于移除Bundle中的所有数据。
Bundle之所以能以键值对的方式存储数据,实质上是因为它的父类BaseBundle内部维护了一个ArrayMap。
|
|
Bundle存取数据的具体实现
下面使用布尔类型的存储源码为例:
|
|
这里的mMap就是ArrayMap,存储数据就是把键值对保存到ArrayMap里。
布尔类型数据的读取源码如下:
|
|
|
|
读取数据的逻辑也很简单,就是通过key从ArrayMap里读出保存的数据,并转换为对应的类型返回,当没找到数据或发生类型转换异常时返回缺省值。
注意到这里有一个方法:unparcel(),它的具体实现如下:
|
|
先来看BaseBundle中mParcelledData的定义:
|
|
在大部分情况下mParcelledData都是null,因此unparcel()直接返回。当使用构造函数public Bundle(Bundle b)创建Bundle时,会给mParcelledData赋值,具体实现如下:
|
|
|
|
从上述代码可以看出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。
上面只是以布尔类型的数据分析了Bundle的存储过程,其他数据类型的存储原理类似,不再赘述。
Bundle容器实现原因
Bundle的容器使用ArrayMap实现而不是HashMap,其原因如下:
- Bundle内部是由ArrayMap实现的,ArrayMap的内部实现是两个数组,一个int数组是存储对象数据对应下标,一个对象数组保存key和value,内部使用二分法对key进行排序,所以在添加、删除、查找数据的时候,都会使用二分法查找,只适合于小数据操作,如果在数据量比较大的情况下,它的性能将退化。而HashMap内部则是数组+链表结构,所以在数据量较少的时候,HashMap的Entry Array比ArrayMap占用更多的内存。因为使用Bundle的场景大多数为小数据量,所以相比之下,在这种情况下使用ArrayMap保存数据,在操作速度和内存占用上都具有优势,因此使用Bundle来传递数据,可以保证更快的速度和更少的内存占用。
- 在android中如果使用Intent来携带数据的话,需要数据是基本类型或者是可序列化类型,HashMap使用Serializable进行序列化,而Bundle则是使用Parcelable进行序列化。在android平台中,更推荐Parcelable实现序列化,虽然写法复杂,但是开销更小,所以为了更加快速地进行数据的序列化和反序列化,系统封装了Bundle类,方便我们进行数据的传输。