未在返回栈顶部的片段已被恢复。

5

给出以下图形和文字描述的应用程序流程。

应用程序流程

  1. 片段1是最低的片段,但通过设置disallowAddToBackStack而不在后退栈中。
  2. 片段2被推入堆栈,使用fragmentTransaction.addToBackStack()
  3. 将片段1的新实例推入堆栈。
  4. 从堆栈中弹出最顶部的片段(片段1)。
  5. 活动2变为前台。
  6. 活动1变为前台。

这里是我用来处理片段的通用方法:

private void changeContainerViewTo(int containerViewId,  Fragment fragment, 
                                   Activity activity, String backStackTag) {

    if (fragmentIsAlreadyPresent(containerViewId, fragment, activity)) { return; }
    final FragmentTransaction fragmentTransaction = 
                 activity.getFragmentManager().beginTransaction();
    fragmentTransaction.replace(containerViewId, fragment);
    fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
    if (backStackTag == null) {
        fragmentTransaction.disallowAddToBackStack();
    } else {
        fragmentTransaction.addToBackStack(backStackTag);
    }
    fragmentTransaction.commit();
}

问题

在上一步中,当活动1恢复时,片段1的最低实例也会恢复。此时,片段1在 getActivity()上返回null

问题

  • 为什么不是堆栈顶部的片段会被恢复?
  • 如果恢复片段是正确的 - 我应该如何处理已分离的片段?

第一个Fragment1和Fragment2是否使用相同的容器视图,并且在第二步中你使用了哪种事务? - Evos
是的,Fragment1和Fragment2使用相同的容器视图。 - JJD
@Evos 所有的事务都是通过 fragmentTransaction.replace(containerViewId, fragment) 进行的。 - JJD
我有一个类似的问题,最好由你的帖子标题描述。你解决了吗? - NeviQ-OR
@NeviQ-OR 有点儿。虽然我已经很久没有详细解释了,但请随意提出一个新问题并在评论中链接它。 - JJD
显示剩余2条评论
3个回答

2
当一个Activity没有显示UI,然后要显示UI时,与之关联的FragmentManager将会销毁所有的fragment并且你需要恢复它们的状态。
正如文档所述:

有很多情况下,fragment可能会被部分销毁(例如当放置在回退栈中而没有显示UI),但是它的状态不会被保存直到其拥有者Activity实际需要保存其状态。

在你的ActivityonSaveInstanceStateonRestoreInstanceState方法中,尝试保存你的Fragment引用,然后使用类似以下代码来恢复它们:
public void onSaveInstanceState(Bundle outState){
    getFragmentManager().putFragment(outState,"myfragment", myfragment);
}
public void onRetoreInstanceState(Bundle inState){
    myFragment = getFragmentManager().getFragment(inState, "myfragment");
}

试着尝试这个,祝你好运!:-)


1
我不明白这种情况是如何发生的,除非(根据您描述的步骤)您误解了fragmentTransaction.addToBackStack()的工作原理:它管理放置在后退堆栈中的事务,而不是片段。
从Android文档中可以看到:
通过调用addToBackStack(),替换事务将保存到后退堆栈中,以便用户可以通过按返回按钮撤消事务并带回上一个片段。
因此,如果您的第2步在代码中看起来像这样:
fragmentTransaction.replace(containerViewId, fragment2);
fragmentTransaction.addToBackStack();
fragmentTransaction.commit();

你的步骤3:

fragmentTransaction.disallowAddToBackStack()//or just no call to addToBackStack - you do not say
fragmentTransaction.replace(containerViewId, newfragment1);
fragmentTransaction.commit();

在这一点上,Fragment2将从后退栈中移除,您的后退栈由两个Fragment1实例组成。在第4步中,您弹出了顶部的实例,这意味着现在应该有底部的Fragment1位于顶部。

这就解释了为什么如果您返回到活动,则它是恢复的片段。但是,我担心它为什么似乎与其活动分离。


我注意到你在调用.replace()之前调用了.disallowAddToBackStack()。请查看我的更新问题。这可能会有问题吗? - JJD
1
不,这只是在构建事务时设置的属性,直到最终调用提交时才使用。 - salfon
如果您当前正在执行以下类似操作的代码: step1)changeContainerViewTo(containerId,afragment1,activity,null) step2)changeContainerViewTo(containerId,afragment2,activity,tag) step3)changeContainerViewTo(containerId,afragment1,activity,null)那么这可能不是您想要的结果 - 尝试在step2的标记参数中传递null,并在step3的标记参数中传递非null值。简而言之,仅当事务中被替换的片段是要保存在返回历史记录中的片段时,才需要调用addToBackStack。 - salfon

1
安卓操作系统会根据需要创建和销毁片段。当您启动活动2并返回活动1时,这很可能会发生。您需要确认当前显示的片段不是正在被销毁的片段。可能发生的情况是,在创建片段2之前,您会看到它执行片段1的某些创建步骤。
至于处理分离的片段,您应该查看页面。要点是,您应该仅在某些片段函数中使用getActivity(基于片段生命周期)。这可能意味着您需要将某些逻辑移动到其他函数中。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接