请原谅长篇文章。我正在玩一个简单的应用程序,并希望将一个自定义对象保存在一个方向更改的片段中。以前在活动中,这曾经使用onRetainNonConfigurationInstance()/ getLastNonConfigurationInstance()方法处理。看到这些方法现在已被弃用,文档鼓励使用片段和setRetainInstance(boolean)方法。
我继续使用这种方法,然后注意到在保持方向变化时碎片状态的行为方面存在奇怪的差异。首先,我正在玩的应用程序的简短解释:
片段A (应用启动时显示的第一个片段)
这是一个包含3个EditText控件的简单片段。每个人在布局文件中都有一个ID。该片段还包括一个按钮,当选择该按钮时,将片段A替换为片段B并将事务保存在后台堆栈上。
片段B
这是一个空布局的片段。如果按下后退,则从后台堆栈恢复片段A.
场景A - setRetainInstance(false):
场景A - setRetainInstance(true):
同样的行为发生在上面
场景B - setRetainInstance(false):
场景B - setRetainInstance(true):
由于某种原因,当方向改变多次时,使用setRetainInstance(true)会干扰片段A(在backstack上)的视图状态。
我开始对setRetainInstance的使用感到紧张,而没有完全理解发生了什么,所以我在支持库源代码中挖掘出来试图解决它。在非常高的层次上,我认为这可能是setRetainInstance(true)发生的事情:
fragmentA.mRemoving
标志设置为true。第一次更改方向。此时FM尝试保存片段的所有状态:
Parcelable saveAllState() {
...
if (f.mState > Fragment.INITIALIZING && fs.mSavedFragmentState == null) {
fs.mSavedFragmentState = saveFragmentBasicState(f);
Fragment A具有CREATED状态并且具有null保存状态,因此它有资格保存其状态。
作为方向更改的一部分,活动将被销毁。长话短说,片段A的状态改为INITIALIZING。
重新创建活动,并尝试将片段的状态移至CREATED。但是,此时需要检查FM moveToState()方法:
if (f.mRemoving && newState > f.mState) {
// While removing a fragment, we can't change it to a higher state.
newState = f.mState;
}
因为片段被保留(未重新创建),因为fragmentA.mRemoving
在步骤1中保持为真,所以它的状态不会增加到CREATED,而是保持在INITIALIZING状态。请注意,即使现在按下后退键,片段A仍将保持其状态不变,因为其状态在步骤2中保存。
第二次更改方向。 FM再次尝试保存片段的所有状态:
Parcelable saveAllState() {
...
if (f.mState > Fragment.INITIALIZING && fs.mSavedFragmentState == null) {
fs.mSavedFragmentState = saveFragmentBasicState(f);
但是,由于片段A处于INITIALIZING状态,因此不符合保存其状态的条件。因此,一旦方向完成第二次,如果按下后退键,则片段A的状态不再完整。
预计会出现这种情况吗?也许这与文件阻止使用setRetainInstance和backstack片段有关?
我们应该如何处理视图状态和setRetainInstance的使用?也许我的用例是不正确的,但是我会紧张地使用setRetainInstance功能来解决这种行为上的差异。
再一次,抱歉这篇长篇文章。反馈将一如既往地受到赞赏。