Bundle源码解析
来源:互联网 发布:javascript定义数组 编辑:程序博客网 时间:2024/06/06 00:46
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的说明如下:
A mapping from String values to various Parcelable types.
官方意为Bundle封装了String值到各种Parcelable类型数据的映射,可见跟我们上述理解是吻合的。
Bundle源码分析
知道了Bundle的主要作用,再来看源码就容易理解了。
Bundle位于android.os
包中,是一个final类,这就注定了Bundle不能被继承。Bundle继承自BaseBundle并实现了Cloneable和Parcelable两个接口,因此对Bundle源码的分析会结合着对BaseBundle源码进行分析。由于实现了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>()
以上代码无需过多解释。
Bundle的几个公有构造方法
第5个构造函数中的PersistableBundle也是继承自BaseBundle的,Bundle还提供了一个静态方法,用来返回只包含一个键值对的Bundle对象,具体源码如下:
public static Bundle forPair(String key, String value) { Bundle b = new Bundle(1); b.putString(key, value); return b;}
Bundle的put与get方法族
Bundle的功能是用来保存数据,那么必然提供了一系列存取数据的方法,这些方法太多了,几乎能够存取任何类型的数据,具体整理为下表:
除了上述存取数据涉及到的方法外,Bundle还提供了一个clear方法:public void clear()
,该方法可用于移除Bundle中的所有数据。
Bundle之所以能以键值对的方式存储数据,实质上是因为它内部维护了一个ArrayMap,具体定义是在其父类BaseBundle中:
ArrayMap<String, ObjectmMap = null;
ArrayMap比HashMap更加高效、更加节省内存,它的初始化是在Bundle的构造方法中实现的,源码如下:
BaseBundle(ClassLoader loader, int capacity) { mMap = capacity > 0 ? new ArrayMap<String, Object>(capacity) : new ArrayMap<String, Object>(); mClassLoader = loader == null ? getClass().getClassLoader() : loader;}
另外还可以发现,存储数据用的key都是String类型,值为各种数据类型,甚至可以存储Bundle、Binder等,如果ArrayMap中存在相同的key,则会替换掉之前的对应值。每一个put方法都对应一个get方法,对于基本数据类型还可以设置缺省值(上述表中未列出对应方法)。具体的存取则是在其父类BaseBundle中实现的,下面就以布尔类型数据为例来分析一下。
Bundle存取数据的具体实现
布尔数据的存储源码如下:
/** * Inserts a Boolean value into the mapping of this Bundle, replacing * any existing value for the given key. Either key or value may be null. * * @param key a String, or null * @param value a Boolean, or null */void putBoolean(String key, boolean value) { unparcel(); mMap.put(key, value);}
这里的mMap就是ArrayMap了,存储数据就是把键值对保存到ArrayMap里。
布尔类型数据的读取源码如下:
/** * Returns the value associated with the given key, or false if * no mapping of the desired type exists for the given key. * * @param key a String * @return a boolean value */boolean getBoolean(String key) { unparcel(); if (DEBUG) Log.d(TAG, "Getting boolean in "+ Integer.toHexString(System.identityHashCode(this))); return getBoolean(key, false);}
getBoolean(String key, boolean defaultValue)的具体实现如下:
/** * Returns the value associated with the given key, or defaultValue if * no mapping of the desired type exists for the given key. * * @param key a String * @param defaultValue Value to return if key does not exist * @return a boolean value */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里读出保存的数据,并转换成对应的类型返回,当没找到数据或发生类型转换异常时返回缺省值。
注意到这里出现了一个方法:unparcel()
,它的具体源码如下:
/** * If the underlying data are stored as a Parcel, unparcel them * using the currently assigned class loader. *//* package */ synchronized void unparcel() { if (mParcelledData == null) { if (DEBUG) Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this)) + ": no parcelled data"); return; } 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; } 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); } mParcelledData.readArrayMapInternal(mMap, N, mClassLoader); mParcelledData.recycle(); mParcelledData = null; if (DEBUG) Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this)) + " final map: " + mMap);}
先来看下BaseBundle中mParcelledData的定义:
/* * 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;
在大部分情况下mParcelledData都是null,因此unparcel()直接返回。当使用构造函数public Bundle(Bundle b)
创建Bundle时,会给mParcelledData赋值,具体实现如下:
/** * Constructs a Bundle containing a copy of the mappings from the given * Bundle. * * @param b a Bundle to be copied. */BaseBundle(BaseBundle b) { if (b.mParcelledData != null) { if (b.mParcelledData == EMPTY_PARCEL) { mParcelledData = EMPTY_PARCEL; } else { mParcelledData = Parcel.obtain(); mParcelledData.appendFrom(b.mParcelledData, 0, b.mParcelledData.dataSize()); mParcelledData.setDataPosition(0); } } else { mParcelledData = null; } if (b.mMap != null) { mMap = new ArrayMap<String, Object>(b.mMap); } else { mMap = null; } mClassLoader = b.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,具体是由以下代码片段实现的:
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); } mParcelledData.readArrayMapInternal(mMap, N, mClassLoader); mParcelledData.recycle(); mParcelledData = null;
上面只是以布尔类型的数据为例分析了Bundle的存取过程,其他数据类型的存取原理相同,就不再赘述。
- Bundle源码解析
- Bundle源码解析
- Bundle解析
- Bundle解析
- Bundle类解析
- 探讨Android源码中Bundle
- Unity3D asset bundle 格式解析
- ViewPager,Bundle,类的解析
- Android Intent Bundle对象解析
- OSGi系列 - 使用Eclipse查看Bundle源码
- Bundle.setClassLoader()方法解析 BootClassLoader PathClassLoader
- Bundle.setClassLoader()方法解析 BootClassLoader PathClassLoader
- Bundle
- Bundle
- Bundle
- Bundle
- BUNDLE
- bundle
- Spring框架总结:
- idea启动项目address localhost:1099 is already in use异常解决
- Spring学习笔记-组件扫描
- 微信JSSDK分享接口,wechat,share ,onMenuShareAppMessage wx.onMenuShareTimeline
- 利用静态内部类实现的单例模式
- Bundle源码解析
- 《Where to Focus: Query Adaptive Matching for Instance Retrieval Using Convolutional Feature Maps》阅读笔
- java解析json格式请求的url中参数
- Effictive Scala(2)
- A1039.Course List for Student
- IPV6地址和报文结构详解
- python学习笔记4
- Firefox浏览器的安装及插件使用
- #python学习笔记# del