Android Fragment状态和setRetainInstance

时间:2013-02-01 12:00:09

标签: android-fragments

请原谅长篇文章。我正在玩一个简单的应用程序,并希望将一个自定义对象保存在一个方向更改的片段中。以前在活动中,这曾经使用onRetainNonConfigurationInstance()/ getLastNonConfigurationInstance()方法处理。看到这些方法现在已被弃用,文档鼓励使用片段和setRetainInstance(boolean)方法。

我继续使用这种方法,然后注意到在保持方向变化时碎片状态的行为方面存在奇怪的差异。首先,我正在玩的应用程序的简短解释:


主要活动

片段A (应用启动时显示的第一个片段)

这是一个包含3个EditText控件的简单片段。每个人在布局文件中都有一个ID。该片段还包括一个按钮,当选择该按钮时,将片段A替换为片段B并将事务保存在后台堆栈上。

片段B

这是一个空布局的片段。如果按下后退,则从后台堆栈恢复片段A.


方案

场景A - setRetainInstance(false):

  1. 显示应用程序并显示片段A.
  2. 我在EditText字段中输入值并选择按钮。
  3. 显示片段B.我改变设备方向一次并按后退键。
  4. 显示片段A,输入的值(视图状态)保持不变。
  5. 场景A - setRetainInstance(true):

    同样的行为发生在上面

    场景B - setRetainInstance(false):

    1. 显示应用程序并显示片段A.
    2. 我在EditText字段中输入值并选择按钮。
    3. 显示片段B.我将设备方向更改两次并点击后退键。
    4. 片段A仍然显示,输入的值(视图状态)保持不变。
    5. 场景B - setRetainInstance(true):

      1. 显示应用程序并显示片段A.
      2. 我在EditText字段中输入值并选择按钮。
      3. 显示片段B.我将设备方向更改两次并点击后退键。
      4. 片段A显示为空的EditText控件,即没有输入的值(视图状态)仍然完整。
      5. 由于某种原因,当方向改变多次时,使用setRetainInstance(true)会干扰片段A(在backstack上)的视图状态。


        可能的解释

        我开始对setRetainInstance的使用感到紧张,而没有完全理解发生了什么,所以我在支持库源代码中挖掘出来试图解决它。在非常高的层次上,我认为这可能是setRetainInstance(true)发生的事情:

        1. 显示片段A,按下按钮,片段A被片段B替换。作为此过程的一部分,FragmentManager(FM)删除片段A,fragmentA.mRemoving标志设置为true。
        2. 第一次更改方向。此时FM尝试保存片段的所有状态:

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

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

        3. 作为方向更改的一部分,活动将被销毁。长话短说,片段A的状态改为INITIALIZING。

        4. 重新创建活动,并尝试将片段的状态移至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中保存。

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

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

          但是,由于片段A处于INITIALIZING状态,因此不符合保存其状态的条件。因此,一旦方向完成第二次,如果按下后退键,则片段A的状态不再完整。


        6. 问题

          1. 预计会出现这种情况吗?也许这与文件阻止使用setRetainInstance和backstack片段有关?

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

          3. 再一次,抱歉这篇长篇文章。反馈将一如既往地受到赞赏。

0 个答案:

没有答案