Bundle、Parcel解析
Bundle 被我们经常用来进行启动Activity、service以及传递数据.Bundle支持的数据类型有原始类型以及其封装类,Parcelable 类型、Size类型、SizeF类型以及Serializable类型。
Bundle基本使用
Intent添加数据
我们先看一下Bundle的添加传递信息的方法,
添加Parcelable类型参数。
添加float类型参数。
通过putParcelable 方法和putFloat 方法可以看出最后数据都会保存在mMap对象中。
mMap的定义可以看出mMap就是一个ArrayMap,存放键值对,而且value的类型是Object类型,就是所有参数的类型都被转换为Object类型保存在mMap中。而系统如何使用这些数据以下再说。
Bundle数据储存
找到Intent数据保存的切入点
通过研究activity的startActivity方法,发现最后会调用ActivityManagerProxy的startActivity方法,该类是ActivityManagerNative的内部类.
|
|
可以看出最后Intent调用了writeToParcel方法,把Bundle中的数据导入进Parcel类型数据中,再看writeToParcel的方法:
|
|
顺着代码调用进入到了Parcel的writeBundle方法,
最后还是回到Bundle类本身。
Parcel最后都都调用write方法保存,最后都会调用本地方法,因为Parcel的数据结构都在c层,所以不做深入研究。有兴趣的可以看看,路径为frameworks\base\core\jni\android_os_Parcel.cpp,以及frameworks\native\libs\binder\Parcel.cpp。
|
|
走到这里,基本可以理清mMap的数据最后都会写入Parcel中,也就是说Bundle也是使用Parcel传递的数据。
要写入Parcel的对象要先判断类型,然后先写入该对象的类型标志,再调用对应类型的方法写入要保存数据。这里需要关注的只有一点,就是Map类型的对象保存,
Parcel只是获取Map的键值对,然后保存,该方法没有保存Map类的信息,也就是类型擦除,最后经过startActivity跳到其他Activity时,再获取时所以Map类型对象都会转换为HashMap对象,所以Bundle传递数据不能使用其他Map类型,比如TreeMap。
Parcel转换为类型对象
这里还是从Intent类看起,Intent实现了Parcelable,ActivityManagerProxy的startActivity方法中intent.writeToParcel(data, 0);通过这个方法Intent把对象的具体信息写入Parcel中,通过一连串的传输最后又通过序列化转为Intent对象。
使用过Parcelable的都知道反序列化最后都会调用包含Parcel参数的构造函数,对于Intent就是Intent(Parcel in),然后调用readFromParcel方法,把Intent的相关属性恢复。对于Bundle数据的恢复是调用了Parcel的readBundle方法。
|
|
Intent的readBundle方法没有直接的把Parcel数据解析为对象数据,还是以Parcel形式保存着,那Parcel是什么时候解析的呢?然后我查看了一下Bundle的get方法,发现最后都调用了unparcel()。
|
|
然后就开始研究unparcel()方法:
|
|
unparcel方法的就是判断当前的Bundle是否含有Parcel数据,然后根据结果创建mMap对象,然后调用readArrayMapInternal把Parcel中的数据发序列化为对象。然后存放在mMap中。
|
|
这里我们可以看到先读取了key,然后调用readValue获取value的值,然后存储到mMap中。我在这里主要分析一下Map类型数据的获取,可以很明了的知道为什么强制为TreeMap会报错。
|
|
一切都很明了,Parcel对Map类型的数据进行反序列化时,获取key\value的值都存放在HashMap中,然后返回HashMap,也就是说所有Map类型数据最后都会转为HashMap类型数据。强制当然会报错。