请原谅长文。我正在玩一个简单的应用程序,并希望在方向更改时将自定义对象保存在片段中。以前在活动中使用 onRetainNonConfigurationInstance() / getLastNonConfigurationInstance() 方法来处理。鉴于这些方法现在已被弃用,文档鼓励使用片段和 setRetainInstance(boolean) 方法。
我继续尝试使用这种方法,然后注意到在跨方向更改保存片段状态时的行为有一个奇怪的差异。首先,对我正在玩的应用程序进行非常简短的解释:
主要活动
片段 A(应用启动时显示的第一个片段)
这是一个带有 3 个 EditText 控件的简单片段。每个在布局文件中都有一个 ID。The fragment also includes a button which when selected replaces Fragment A with Fragment B and saves the transaction on the backstack.
片段 B
这是一个布局为空的片段。如果 back 被推回,Fragment A 从 backstack 中恢复。
场景
场景 A - setRetainInstance(false):
- 应用启动并显示片段 A。
- 我在 EditText 字段中输入值并选择按钮。
- 显示片段 B。我更改了一次设备方向并按了返回键。
- 显示片段 A 时输入的值(视图状态)保持不变。
场景 A - setRetainInstance(true):
发生与上述相同的行为
场景 B - setRetainInstance(false):
- 应用启动并显示片段 A。
- 我在 EditText 字段中输入值并选择按钮。
- 显示片段 B。我两次更改设备方向并按后退键。
- 片段 A 仍然显示完整的输入值(视图状态)。
场景 B - setRetainInstance(true):
- 应用启动并显示片段 A。
- 我在 EditText 字段中输入值并选择按钮。
- 显示片段 B。我两次更改设备方向并按后退键。
- 片段 A 显示为空的 EditText 控件,即没有一个输入的值(视图状态)仍然完好无损。
出于某种原因,当方向更改不止一次时,使用 setRetainInstance(true) 会干扰片段 A(在后台堆栈上)的视图状态。
可能的解释
我开始对 setRetainInstance 的使用感到紧张,但对发生的事情没有完全了解,因此我在支持库源代码中进行了挖掘以尝试弄清楚。在非常高的层次上,我认为这可能是 setRetainInstance(true) 的情况:
显示片段 A,按下按钮并将片段 A 替换为片段 B。作为此过程的一部分,片段管理器 (FM) 删除片段 A 并将
fragmentA.mRemoving
标志设置为真。第一次改变方向。此时 FM 尝试保存片段的所有状态:
Parcelable saveAllState() { ... if (f.mState > Fragment.INITIALIZING && fs.mSavedFragmentState == null) { fs.mSavedFragmentState = saveFragmentBasicState(f);
片段 A 具有 CREATED 状态并且具有 null 已保存状态,因此它有资格保存其状态。
活动作为方向改变的一部分被破坏。长话短说,Fragment A 的状态变为 INITIALIZING。
重新创建活动并尝试将片段的状态移动到已创建。但是,此时在 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 处于初始化状态,它没有资格保存其状态。因此,一旦第二次定向完成,如果按下返回键,片段 A 的状态就不再完整。
问题
这种行为是预期的吗?也许这与不鼓励使用 setRetainInstance 和 backstack 片段的文档有关?
我们应该如何处理视图状态和 setRetainInstance 的使用?也许我的用例不正确,但使用 setRetainInstance 功能和这种行为差异我会很紧张。
再一次,很抱歉发了这么长的帖子。反馈将一如既往地受到赞赏。