1

请原谅长文。我正在玩一个简单的应用程序,并希望在方向更改时将自定义对象保存在片段中。以前在活动中使用 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):

  1. 应用启动并显示片段 A。
  2. 我在 EditText 字段中输入值并选择按钮。
  3. 显示片段 B。我更改了一次设备方向并按了返回键。
  4. 显示片段 A 时输入的值(视图状态)保持不变。

场景 A - setRetainInstance(true):

发生与上述相同的行为

场景 B - setRetainInstance(false):

  1. 应用启动并显示片段 A。
  2. 我在 EditText 字段中输入值并选择按钮。
  3. 显示片段 B。我两次更改设备方向并按后退键。
  4. 片段 A 仍然显示完整的输入值(视图状态)。

场景 B - setRetainInstance(true):

  1. 应用启动并显示片段 A。
  2. 我在 EditText 字段中输入值并选择按钮。
  3. 显示片段 B。我两次更改设备方向并按后退键。
  4. 片段 A 显示为空的 EditText 控件,即没有一个输入的值(视图状态)仍然完好无损。

出于某种原因,当方向更改不止一次时,使用 setRetainInstance(true) 会干扰片段 A(在后台堆栈上)的视图状态。


可能的解释

我开始对 setRetainInstance 的使用感到紧张,但对发生的事情没有完全了解,因此我在支持库源代码中进行了挖掘以尝试弄清楚。在非常高的层次上,我认为这可能是 setRetainInstance(true) 的情况:

  1. 显示片段 A,按下按钮并将片段 A 替换为片段 B。作为此过程的一部分,片段管理器 (FM) 删除片段 A 并将fragmentA.mRemoving标志设置为真。

  2. 第一次改变方向。此时 FM 尝试保存片段的所有状态:

    Parcelable saveAllState() {
    ...
    if (f.mState > Fragment.INITIALIZING && fs.mSavedFragmentState == null) {
                fs.mSavedFragmentState = saveFragmentBasicState(f);
    

    片段 A 具有 CREATED 状态并且具有 null 已保存状态,因此它有资格保存其状态。

  3. 活动作为方向改变的一部分被破坏。长话短说,Fragment A 的状态变为 INITIALIZING。

  4. 重新创建活动并尝试将片段的状态移动到已创建。但是,此时在 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 中保存。

  5. 第二次改变方向。FM 再次尝试保存片段的所有状态:

    Parcelable saveAllState() {
    ...
    if (f.mState > Fragment.INITIALIZING && fs.mSavedFragmentState == null) {
                fs.mSavedFragmentState = saveFragmentBasicState(f);
    

    但是,由于片段 A 处于初始化状态,它没有资格保存其状态。因此,一旦第二次定向完成,如果按下返回键,片段 A 的状态就不再完整。


问题

  1. 这种行为是预期的吗?也许这与不鼓励使用 setRetainInstance 和 backstack 片段的文档有关?

  2. 我们应该如何处理视图状态和 setRetainInstance 的使用?也许我的用例不正确,但使用 setRetainInstance 功能和这种行为差异我会很紧张。

再一次,很抱歉发了这么长的帖子。反馈将一如既往地受到赞赏。

4

0 回答 0