为什么即使我没有实现必要的功能,Parcelable 也能工作?
Why does Parcelable work even though I did not implement the necessary functions?
我想在屏幕旋转期间保留一个复杂的 java 对象,所以我将对象设为 Parcelable 并实现了必要的方法:
- 在 writeToParcel(Parcel dest, int flags) 方法中,我将一些值保存到“dest”。
- 在 Parcelable.Creator 的 createFromParcel(Parcel source) 方法中,我以正确的顺序从“源”获取值并返回了适当的对象。
然后在 Fragment 的 onSaveInstanceState 中我保存了 Parcelable :
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putParcelable("myObject", myObject);
}
并在 Fragment 的 onCreate 中获取我的对象:
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MyObject myObject = savedInstanceState.getParcelable("myObject");
}
效果很好。
然后我做了如下测试:
- 删除了我在 writeToParcel 方法中的所有代码。
- 在 createFramParcel 方法中返回 null。
当我 运行 应用程序时,我得到了完全相同的结果!我得到了一个包含所有适当值的对象。
为什么这样做? Parcelable 是否“自动”创建 Parcelable 对象?
是的,所以这与 Bundle 在内部处理缓存和打包的方式有关。当您调用 putParcelable()
时,它 运行 的代码如下:
public void putParcelable(@Nullable String key, @Nullable Parcelable value) {
unparcel();
mMap.put(key, value);
mFdsKnown = false;
}
所以基本上,Bundle
中的数据不会立即写入 Parcel
-- mMap
是 ArrayMap<String, Object>
,它包含所有对象的缓存在 Bundle
中插入或删除它们。
在某个时候,writeToParcel()
将在 Bundle
上调用,此时 mMap
中的所有内容都会写入 mParcelledData
。
所以基本上,当您进行配置更改时,Bundle
仍未写入 Parcel
,因此您传入的对象的相同实例仍存储在Bundle
的 mMap
(因此您的对象也从未调用过 writeToParcel()
——您可以通过断言配置更改前后的对象具有相同的 System.identityHashCode()
来确认这一点).
您可以在 BaseBundle
中看到关于此的注释:
// Invariant - exactly one of mMap / mParcelledData will be null
// (except inside a call to unparcel)
ArrayMap<String, Object> mMap = null;
/*
* 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;
因此,如果您要将 Parcelable
对象写入保存状态包,并将您的应用程序置于后台直到进程终止(或者我相信您可以通过 运行ning 强制执行此操作adb shell am kill <application_id>
) 然后继续,您将 运行 遇到数据未正确打包的问题。
我想在屏幕旋转期间保留一个复杂的 java 对象,所以我将对象设为 Parcelable 并实现了必要的方法:
- 在 writeToParcel(Parcel dest, int flags) 方法中,我将一些值保存到“dest”。
- 在 Parcelable.Creator 的 createFromParcel(Parcel source) 方法中,我以正确的顺序从“源”获取值并返回了适当的对象。
然后在 Fragment 的 onSaveInstanceState 中我保存了 Parcelable :
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putParcelable("myObject", myObject);
}
并在 Fragment 的 onCreate 中获取我的对象:
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MyObject myObject = savedInstanceState.getParcelable("myObject");
}
效果很好。
然后我做了如下测试:
- 删除了我在 writeToParcel 方法中的所有代码。
- 在 createFramParcel 方法中返回 null。
当我 运行 应用程序时,我得到了完全相同的结果!我得到了一个包含所有适当值的对象。
为什么这样做? Parcelable 是否“自动”创建 Parcelable 对象?
是的,所以这与 Bundle 在内部处理缓存和打包的方式有关。当您调用 putParcelable()
时,它 运行 的代码如下:
public void putParcelable(@Nullable String key, @Nullable Parcelable value) {
unparcel();
mMap.put(key, value);
mFdsKnown = false;
}
所以基本上,Bundle
中的数据不会立即写入 Parcel
-- mMap
是 ArrayMap<String, Object>
,它包含所有对象的缓存在 Bundle
中插入或删除它们。
在某个时候,writeToParcel()
将在 Bundle
上调用,此时 mMap
中的所有内容都会写入 mParcelledData
。
所以基本上,当您进行配置更改时,Bundle
仍未写入 Parcel
,因此您传入的对象的相同实例仍存储在Bundle
的 mMap
(因此您的对象也从未调用过 writeToParcel()
——您可以通过断言配置更改前后的对象具有相同的 System.identityHashCode()
来确认这一点).
您可以在 BaseBundle
中看到关于此的注释:
// Invariant - exactly one of mMap / mParcelledData will be null
// (except inside a call to unparcel)
ArrayMap<String, Object> mMap = null;
/*
* 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;
因此,如果您要将 Parcelable
对象写入保存状态包,并将您的应用程序置于后台直到进程终止(或者我相信您可以通过 运行ning 强制执行此操作adb shell am kill <application_id>
) 然后继续,您将 运行 遇到数据未正确打包的问题。