Android FragmentManagerImpl.dispatchResume() 恢复片段顺序不对



我在Android中遇到了一个非常奇怪的问题,我不知道为什么会发生这种情况或如何编码。 我真的相信这是一个Android错误。

我有一个 MainActivity,其中包含一个名为 main_container 的 FrameLayout(它的高度和宽度都match_parent因为每个片段都应该是唯一"显示"给用户的片段)。 从 MainActivity 中,我添加了片段 A,如下所示:

mFragmentManager.beginTransaction()
        .replace(R.id.main_container, frag, fragTag)
        .commit();

从那里,片段 A 在用户点击视图时,将添加片段 B,如下所示("frag"和"fragTag"是与上述代码片段不同的值):

mFragmentManager.beginTransaction()
        .setCustomAnimations(R.anim.slide_in_right, 0, 0, R.anim.slide_out_right)
        .add(R.id.main_container, frag, fragTag)
        .addToBackStack(null)
        .commit();

从这里开始,片段 B 将像这样添加片段 C(同样,"frag" 和 "fragTag" 是与前两个片段不同的值):

mFragmentManager.beginTransaction()
        .setCustomAnimations(R.anim.slide_in_right, 0, 0, R.anim.slide_out_right)
        .add(R.id.main_container, frag, fragTag)
        .addToBackStack(null)
        .commit();

所以在这一点上,在后台堆栈中,我们应该有片段 A ->片段 B ->片段 C。

片段 C 在用户单击视图时调用媒体选取器。 这样做会调用所有片段的 onPause 方法,并将应用程序置于后台。 现在,当用户选择图像时,应用程序将恢复,但这是错误发生的地方......它按此顺序恢复,正如每个片段的 onResume 方法中的断点所证明的那样:

片段 A -> 片段

C -> 片段 B

这会导致各种问题,因为我的每个片段都注册为主活动中的侦听器来处理后退按钮单击。 此逻辑依赖于该顺序的正确性。 出于某种原因,它仍然在顶部显示片段 C,但 onResume 肯定是乱序调用的。

也许更糟糕的是...与其单击媒体选择器,不如简单地旋转电话以引起配置更改。 这表现出与重新排序为 A -> C> B 相同的行为,但在这种情况下,它实际上确实在顶部显示了错误的片段。 它在顶部显示片段 B。

这是一个设计点,你不能依靠Android来恢复片段,就像你将它们添加到backstack并且我应该围绕它编码一样? 还是我做错了什么? 或者这真的是一个安卓错误? 到目前为止,我不是Android开发的新手,但这个让我难倒了。

编辑:

我已经确定了正在发生的事情,显然这是设计使然。 这对我来说似乎很疯狂,我不同意它背后的逻辑。 我也许可以通过反思来解决这个问题,但我不喜欢这样做。 无论如何,关于问题。

问题在于FragmentManagerImpl跟踪活动片段的方式。 它有一个 ArrayList 来跟踪活动的片段,当所有内容都暂停时(例如,在我的情况下,我开始打算从媒体库中获取照片,因此它离开了我的应用程序),在恢复到我的应用程序中时,它会按照它们在该 ArrayList 中的相同顺序将片段移回活动状态。 听起来很棒,嗯?

好吧,这是我的问题。 当东西从该 ArrayList 中取出时,它们不会删除() 该项,它们只是将其设置为 null,然后有逻辑在下一个片段出现时重用该空"插槽"(github 链接中的第 1168 行)。 就我而言,在 ArrayList 中留下漏洞的瞬态片段是 DialogFragment。 回到我的原始报告,片段A显示了一个对话片段...单击该对话片段中的某个按钮将显示片段 B。 单击片段 B 中的另一个视图将显示片段 C。 但以下是 ArrayList FragmentManagerImpl 在单击对话框片段中的按钮后

跟踪的情况:

{ FragA, null (曾经是 DialogFragment), FragB }

因此,显然在FragB移至活动状态后,DialogFragment被移出了活动状态,从而留下了一个漏洞。 所以现在我们点击FragB中的视图来调出FragC,ArrayList看起来像这样:

{ FragA, FragC (reuse DialogFragment's slot), FragB }

我们去媒体选择器,回来,瞧,关于我最初如何实例化它们,片段被无序地恢复。 这对我来说毫无意义,如果你不进入带有断点的操作系统代码,你永远不会弄清楚为什么 Android 的行为方式不符合你告诉它的方式。 似乎只对你删除的片段做一个 ArrayList.remove() 会更容易,这样就不会留下任何漏洞。

就像我说的,我可能可以通过反思来解决这个问题......但我对此持怀疑态度,因为所有片段中也有这个 mIndex 变量对应于该 ArrayList (mActive) 中插槽的索引。 所以我必须确保保持同步......现在我依赖于了解操作系统代码的工作原理。:(

这是一个已知问题。 谷歌"android片段重新排序",您将获得有关该主题的整页链接,包括一些解决方案。

最新更新