【问题标题】:Fragment recreated every time after orientation change, unable to restore state每次方向改变后都会重新创建片段,无法恢复状态
【发布时间】:2014-01-24 14:09:17
【问题描述】:

更新:事实证明问题出在其他地方。感谢@Luksprog 指出我忽略的内容。

  1. 该项目是使用 Android Studio 的抽屉式导航模式创建的。抽屉在NavigationDrawerFragment 类中实现。
  2. The fragment holding the view pager is added when a particular item in the drawer is selected.代码实现了我的家庭活动。
  3. 当屏幕旋转时,调用NavigationDrawerFragmentonCreate()方法,保留最后选择的项目。
  4. 这就是问题所在 - 重新创建时,NavigationDrawerFragment 将再次调用 selectItem(),这会触发我的菜单项选择处理程序。这会导致 Android 恢复 ListFragment。

这可以通过检查我的菜单选择处理程序代码中的活动菜单项来防止。


无论出于何种原因(例如方向改变。

ViewPager 位于附加到活动的Fragment(名为ListFragment)中。我使用的是compat库,所以片段是android.support.v4.app.Fragment的子类。

我认为可以通过覆盖onSaveInstanceState() 方法并在onCreate() 中添加适当的逻辑来完成,如doc 中所述:

要正确处理重启,重要的是您的活动 通过正常的 Activity 生命周期恢复其先前的状态,在 哪个Android在销毁您的之前调用 onSaveInstanceState() 活动,以便您可以保存有关应用程序状态的数据。你 然后可以在 onCreate() 期间恢复状态或 onRestoreInstanceState()。

但片段的情况似乎有所不同。当我从这个ListFragment 导航到另一个活动并按下“返回”时,可以正确恢复页面索引。但是,当我旋转我的设备时,页面索引会丢失。

我添加了一些日志来查看问题所在。从日志中我发现虽然ListFragment(我称之为ListFragment A)中的onSaveInstanceState() 被正确调用,但是这个特定的Fragment 类不再显示在活动中。当方向改变并重新创建活动时,Android 调用onSaveInstanceState() 后跟onDetach() 来分离这个片段。然后Android 创建一个ListFragment 的新实例(我称之为ListFragment B)并将它附加到新的、旋转的活动上。这个 ListFragment B 有一个空的 savedInstanceState 传递给构造函数,因此最后一页索引(以及 Fragment A 的 savedInstanceState 中的任何配置)都丢失了。

实际上,每次屏幕旋转都会创建一个ListFragment的新实例,但似乎不会破坏旧的实例。旋转设备时,我看到如下日志:

D/ListFragment﹕ [1110257048] onSaveInstanceState() called, storing last page index 3
D/ListFragment﹕ [1109835992] onSaveInstanceState() called, storing last page index 0
D/ListFragment﹕ [1108826176] onSaveInstanceState() called, storing last page index 0
D/ListFragment﹕ [1108083096] onSaveInstanceState() called, storing last page index 0
D/ListFragment﹕ [1106541040] onSaveInstanceState() called, storing last page index 0
D/ListFragment﹕ [1108316656] onSaveInstanceState() called, storing last page index 0
D/ListFragment﹕ [1109134136] onSaveInstanceState() called, storing last page index 0
D/ListFragment﹕ [1108630992] onSaveInstanceState() called, storing last page index 0
D/ListFragment﹕ [1108592888] onSaveInstanceState() called, storing last page index 0
D/ListFragment﹕ [1109729064] onSaveInstanceState() called, storing last page index 0
D/ListFragment﹕ [1110257048] onDestroy()
D/ListFragment﹕ [1110257048] onDetach()
D/ListFragment﹕ [1109835992] onDestroy()
D/ListFragment﹕ [1109835992] onDetach()
D/ListFragment﹕ [1108826176] onDestroy()
D/ListFragment﹕ [1108826176] onDetach()
D/ListFragment﹕ [1108083096] onDestroy()
D/ListFragment﹕ [1108083096] onDetach()
D/ListFragment﹕ [1106541040] onDestroy()
D/ListFragment﹕ [1106541040] onDetach()
D/ListFragment﹕ [1108316656] onDestroy()
D/ListFragment﹕ [1108316656] onDetach()
D/ListFragment﹕ [1109134136] onDestroy()
D/ListFragment﹕ [1109134136] onDetach()
D/ListFragment﹕ [1108630992] onDestroy()
D/ListFragment﹕ [1108630992] onDetach()
D/ListFragment﹕ [1108592888] onDestroy()
D/ListFragment﹕ [1108592888] onDetach()
D/ListFragment﹕ [1109729064] onDestroy()
D/ListFragment﹕ [1109729064] onDetach()
D/ListFragment﹕ [1110903656] onAttach()
D/ListFragment﹕ [1110903656] onCreate()
D/ListFragment﹕ [1110903656] savedInstanceState is not NULL.
D/ListFragment﹕ [1110903656] Retrieving last page index 3
D/ListFragment﹕ [1110905248] onAttach()
D/ListFragment﹕ [1110905248] onCreate()
D/ListFragment﹕ [1110905248]   savedInstanceState is not NULL.
D/ListFragment﹕ [1110905248]   Retrieving last page index 0
D/ListFragment﹕ [1110906440] onAttach()
D/ListFragment﹕ [1110906440] onCreate()
D/ListFragment﹕ [1110906440]   savedInstanceState is not NULL.
D/ListFragment﹕ [1110906440]   Retrieving last page index 0
D/ListFragment﹕ [1110907632] onAttach()
D/ListFragment﹕ [1110907632] onCreate()
D/ListFragment﹕ [1110907632]   savedInstanceState is not NULL.
D/ListFragment﹕ [1110907632]   Retrieving last page index 0
D/ListFragment﹕ [1110908824] onAttach()
D/ListFragment﹕ [1110908824] onCreate()
D/ListFragment﹕ [1110908824]   savedInstanceState is not NULL.
D/ListFragment﹕ [1110908824]   Retrieving last page index 0
D/ListFragment﹕ [1110910016] onAttach()
D/ListFragment﹕ [1110910016] onCreate()
D/ListFragment﹕ [1110910016]   savedInstanceState is not NULL.
D/ListFragment﹕ [1110910016]   Retrieving last page index 0
D/ListFragment﹕ [1110911208] onAttach()
D/ListFragment﹕ [1110911208] onCreate()
D/ListFragment﹕ [1110911208]   savedInstanceState is not NULL.
D/ListFragment﹕ [1110911208]   Retrieving last page index 0
D/ListFragment﹕ [1110912400] onAttach()
D/ListFragment﹕ [1110912400] onCreate()
D/ListFragment﹕ [1110912400]   savedInstanceState is not NULL.
D/ListFragment﹕ [1110912400]   Retrieving last page index 0
D/ListFragment﹕ [1110913592] onAttach()
D/ListFragment﹕ [1110913592] onCreate()
D/ListFragment﹕ [1110913592]   savedInstanceState is not NULL.
D/ListFragment﹕ [1110913592]   Retrieving last page index 0
D/ListFragment﹕ [1110914784] onAttach()
D/ListFragment﹕ [1110914784] onCreate()
D/ListFragment﹕ [1110914784]   savedInstanceState is not NULL.
D/ListFragment﹕ [1110914784]   Retrieving last page index 0
D/HomeActivity﹕ fragment updated
D/ListFragment﹕ [1110914784] onCreateView()
D/ListFragment﹕ [1111031048] onAttach()
D/HomeActivity﹕ Fragment attached.
D/ListFragment﹕ [1111031048] onCreate()
D/ListFragment﹕ [1111031048]   savedInstanceState is NULL.
D/ListFragment﹕ [1111031048] onCreateView()
D/ListFragment﹕ [1111031048] onResume(), restoring page index 0

这是我旋转屏幕大约 10 次后的日志。标签中的数字是类的hashCode()。以上几行表明,即使在将它们替换为最新的 (1111031048) 片段后,先前创建的片段的 onSaveInstanceState()onCreate() 仍然会被调用。

请注意,我没有在片段类中调用setRetainInstance()。事实上,setRetainInstance(false)setRetainInstance(true) 我都试过了,但没有任何改变。

我在这里做错了吗?我可以理解ListFragment 需要重新创建,但为什么savedInstanceState 为空?如果这是预期的行为,解决我的需求的正确方法是什么,即在配置更改时保持页面索引?

应该可以使页面索引成为静态类变量,但我不确定它是否真的解决了问题,或者只是隐藏了它(因为我在上面的日志中闻到了内存泄漏)。

【问题讨论】:

  • 您是否在所有被覆盖的活动和片段方法(如onCreateonSaveInstanceState)等上调用super
  • 您确定您的代码在旋转后不会再次(错误地)添加包含ViewPager 的片段,替换保存的片段吗?
  • @Luksprog 你是绝对正确的。我太专注于片段,而忽略了这部分。我会更新我的帖子。非常感谢!
  • 您应该发布答案并接受它(对于您的其他问题以及如果您得到正确的答案),这样问题就会显示为已解决。
  • 谢谢。对于像这样的愚蠢错误,我不确定我是否适合回答我自己的问题(并将其标记为答案),但我听从了您的建议,希望它很快会对某人有所帮助。

标签: android android-fragments android-configchanges


【解决方案1】:

即使答案已经被接受,让我再澄清一下:“问题”在 Android Studio 模板中。正如 Edmund 所指出的,问题在于导航抽屉在重新创建时调用菜单,从而重新调用 Fragment。 为了解决这个问题,我按照 Android Studio 的建议对 NavigationDrawerFragment.java 文件进行了轻微修改。 原文:

    @Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // Read in the flag indicating whether or not the user has demonstrated awareness of the
    // drawer. See PREF_USER_LEARNED_DRAWER for details.
    SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getActivity());
    mUserLearnedDrawer = sp.getBoolean(PREF_USER_LEARNED_DRAWER, false);

    if (savedInstanceState != null) {
        mCurrentSelectedPosition = savedInstanceState.getInt(STATE_SELECTED_POSITION);
        mFromSavedInstanceState = true;
    }

    // Select either the default item (0) or the last selected item.
    selectItem(mCurrentSelectedPosition);

}

新的:

    @Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // Read in the flag indicating whether or not the user has demonstrated awareness of the
    // drawer. See PREF_USER_LEARNED_DRAWER for details.
    SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getActivity());
    mUserLearnedDrawer = sp.getBoolean(PREF_USER_LEARNED_DRAWER, false);

    if (savedInstanceState != null) {
        mCurrentSelectedPosition = savedInstanceState.getInt(STATE_SELECTED_POSITION);
        mFromSavedInstanceState = true;
    } else {
        // Select either the default item (0) or the last selected item.
        selectItem(mCurrentSelectedPosition);
    }
}

刚刚浪费了半天的制作时间来解决这个问题。希望它可以帮助其他人。

【讨论】:

  • 这对我帮助很大。我试图弄清楚如何防止在屏幕旋转时重新创建片段。谢谢!
  • 非常感谢您
  • 太棒了!这正是我一直在寻找的。我想知道为什么预建的导航抽屉没有为配置更改做好充分准备。它有帮助!谢谢!
【解决方案2】:

正如问题中的更新,这已解决,并再次感谢@Luksprog 指出我忽略的内容。

Fragment 的行为实际上与Activity 类一致。

这是我的问题的原因:

  • 该项目是使用 Android Studio 的“创建项目”向导提供的导航抽屉模式创建的。抽屉在NavigationDrawerFragment 类中实现。
  • The fragment holding the view pager is added when a particular item in the drawer is selected.代码实现了我的家庭活动。
  • 屏幕旋转时,调用NavigationDrawerFragmentonCreate()方法,保留最后选择的项目。
  • 这就是问题所在 - 重新启动时,NavigationDrawerFragment 将再次调用 selectItem(),这会触发我的菜单项选择处理程序。这会导致 ListFragment 被替换。

这可以通过检查我的菜单选择处理程序代码中的活动菜单项或禁用 selectItem() 调用来防止。

【讨论】:

    【解决方案3】:

    您不需要处理已保存的InstanceState。因为存储在片段管理器中的所有片段(它就像片段数组)并且当方向改变时活动会破坏,但片段管理器仍然保存之前添加的所有片段。所以你只需要检查是否已经添加了片段。

    private void openFragment(Fragment fragment, String tag) {
                FragmentManager fragmentManager = getSupportFragmentManager();
                FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
                Fragment existingFragment = fragmentManager.findFragmentByTag(tag);
                if (existingFragment != null) {
                    fragmentTransaction.add(R.id.container, fragment, tag);
                    fragmentTransaction.commit();
                }
            }
    

    【讨论】:

      【解决方案4】:

      尝试在此处使用 onCreateView() 而不是 onCreate(),后者是 Fragments 特有的,可能会有所作为。

      覆盖onSaveInstanceState() 以存储您的状态,并使用savedInstanceState.get*()onCreateView() 中恢复它。这就是我们在应用程序中的做法,它应该可以工作。正如@Ari 提到的,请务必致电super.onSaveInstanceState(outState)

      class MyFragment extends ListFragment {
      
          @Override
          public void onSaveInstanceState(Bundle outState) {
              outState.putString(ARG_PAGE, page); 
              super.onSaveInstanceState(outState);
          }
      
          @Override
          public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
              if(savedInstanceState != null){
                  if(savedInstanceState.containsKey(ARG_PAGE)){
                      page = savedInstanceState.getInteger(ARG_PAGE);
                  }
              }
          }
      }
      

      【讨论】:

        【解决方案5】:

        在AndroidManifest.xml中添加

        <activity android:name=".MainActivity"
        android:configChanges="orientation|screenSize">
        </activity>
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-08-27
          • 1970-01-01
          • 2013-04-01
          相关资源
          最近更新 更多