【问题标题】:Fragment lifecycle overlap on navigate导航上的片段生命周期重叠
【发布时间】:2018-12-11 22:12:17
【问题描述】:

我有一个 Activity 应用程序和多个 Fragments 正在使用导航组件进行切换。当我在两个片段之间切换时,它们的 onCreate()onDestroy() 方法似乎重叠。因此,当片段访问相同的全局对象时,我很难为片段编写初始化和清理代码。

Framgent_A 导航到Fragment_B 具有以下方法顺序:

Fragment_B.onCreate()
Fragment_A.onDestroy()

Fragment_A.onDestroy() 中,我反转了我在Fragment_A.onCreate() 中执行的操作。在Fragment_B 中,我希望在调用onCreate() 时事情处于中立状态。但情况并非如此,因为尚未调用 Fragment_A.onDestroy()

Android 上的重叠是正常的还是我在导航组件中配置了错误?有没有另一种方法可以实现我想要做的事情?我知道我可以将两者结合起来Fragments 并使其工作,但我不希望任何一个 Fragment 彼此了解。对我来说,当Fragment_B 被创建时Framgnet_A 仍然活着,而Fragment_B 应该替换Fragment_A 时,这似乎很奇怪。

非常感谢任何帮助!


编辑:

在调试的同时浏览源代码后,我发现在FragmentNavigator.navigate() 中调用了FragmentTransaction.setReorderingAllowed(),这允许重新排序操作,甚至允许在onDestroy() 之前调用新片段的onCreate()以前的。问题仍然存在,在下一个 Fragment 中初始化相同的全局状态之前,如何解决我在一个 Fragment 中正确清理全局状态的问题。

【问题讨论】:

  • Fragment B 和 Fragment A 的 onCreate 和 onDestroy 里面有什么代码?是否在那里完成了耗时的计算?
  • @AADProgramming 它们可以为空,它们的执行顺序保持不变。
  • @rozina 想一想,他们都需要活着才能让画面转场看起来无懈可击,否则转场之间有黑屏的可能吗?仍然没有回答这个问题,因为我和你在同一条船上(媒体资源使用 onPause 和 onResume)
  • 如果没有代码,它只是在猜测,但是 1) 你在 onCreate() 中做的事情会在生命周期的后面做得更好(onResume() / onActivityCreated()?2)如果两个片段正在创建或清理一些共同点,然后它们之间存在一些耦合。是否可以由父 Activity 或 Fragment 协调创建和清理?
  • 您的全局对象需要访问/清理什么?你应该做还是不做的想法?两个片段都需要通过活动相互通信,而不是相互依赖。如果您可以在这里发布您的代码,那就太好了

标签: android android-architecture-components android-jetpack android-architecture-navigation fragment-lifecycle


【解决方案1】:

Android Fragment 生命周期并不是真正适合您需求的回调主机。导航控制器将用动画替换这两个片段,因此两者都以某种方式同时可见,最终甚至退出片段的onPause() 在进入片段的onResume() 之后被调用。

解决方案 1:使用OnDestinationChangedListener

onDestinationChanged() 回调在任何生命周期事件之前调用。作为一种非常简化的方法(注意泄漏),您可以执行以下操作:

    findNavController().addOnDestinationChangedListener { _, destination, _ ->
        if(shouldCleanupFor(destination)) cleanup()
    }

解决方案 2:将全局更改抽象掉

与其让单个导航点改变全局状态,不如为它提供一个真实点。这可能是独立于导航层次结构的另一个片段。然后像以前一样观察导航:

findNavController(R.id.nav_graph).addOnDestinationChangedListener { _, destination, _ ->
    resetAll()
    when(distination.id) {
        R.id.fragment_a -> prepareForA()
        R.id.fragment_b -> prepareForB()
        else -> prepareDefault()
    }
}

作为一个额外的优势,您还可以幂等地实现状态更改。

【讨论】:

  • 无论好坏,导航库在导航过程中改变了片段的默认行为,因此您需要使用上述解决方法或不使用导航库 - issuetracker.google.com/issues/155574963
  • 与使用 onViewCreated() onDestroyView() 改变应用程序的行为不同,这种方法效果更好,而且是一个更简洁的解决方案。
【解决方案2】:

由于您有一个控制 Fragment 膨胀的 Activity,因此您可以手动控制正在膨胀的 Fragment 的生命周期。通过调用以下方法,您可以控制哪个片段准备好使用全局数据。此时您将不得不,一些如何将数据传递回 Mainactivity 以确定哪个片段是活动的,因为您询问如何同时膨胀 2 个片段,这将共享一个对象。更好的方法是让 MainActivity 使用特定的类实现 FragmentA 和 FragmentB-detail 来执行 Stuff,这样您必须像平板电脑一样对待您的应用程序并确定 2 窗格模式以及哪一点您可以从您控制的那些片段中使用适当的类活动。包含的link matches what your trying to accomplish

private void addCenterFragments(Fragment fragment) {
        try {
            removeActiveCenterFragments();
            fragmentTransaction = fragmentManager.beginTransaction();
            fragmentTransaction.add(R.id.content_fragment, fragment);
            fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
            activeCenterFragments.add(fragment);
            fragmentTransaction.commit();
        }catch (Exception e){
            Crashlytics.logException(e);
        }
    }

    private void removeActiveCenterFragments() {
        if (activeCenterFragments.size() > 0) {
            fragmentTransaction = fragmentManager.beginTransaction();
            for (Fragment activeFragment : activeCenterFragments) {
                fragmentTransaction.remove(activeFragment);
            }
            activeCenterFragments.clear();
            fragmentTransaction.commit();
        }
    }

【讨论】:

  • 我正在使用新的导航组件,它为我执行片段事务。否则是一种有效的方法。
【解决方案3】:

也许您可以将一些与初始化相关的代码移动到该片段onStart()onCreateView() 方法中。根据the developer documentation,这是应该进行初始化的地方。

另一个可用的选项是使用Observer /Observable 模式,您可以在片段A 中的onDestroy() 完成后通知您的活动。然后 Activity 会通知 Fragment B 可以安全地假设清理状态并开始初始化。

【讨论】:

    【解决方案4】:

    我的情况有点不同,我想分享一下,以防有人遇到同样的问题。

    我想在当前片段的onPause() 中执行一项操作,但在从一个片段导航到另一个片段时不执行该代码。我要做的是调用isRemoving() 方法来检查当前片段是否被删除。调用NavController.navigate(...)方法时设置为true。

    override fun onPause() {
        super.onPause()
        if (!isRemoving()) {
            // Write your code here
        }
    }
    

    根据 Google 的 Fragment.isRemoving() 文档:

    如果当前正在从其活动中删除此片段,则返回 true。这不是它的活动是否正在完成,而是它是否正在从它的活动中移除。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-07-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-08-22
      相关资源
      最近更新 更多