片段生命周期在导航上重叠

时间:2018-07-03 14:35:58

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

我有一个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彼此了解。在我看来,Framgnet_A在创建Fragment_B时仍然有效,当Fragment_B应该替换Fragment_A时仍然如此。

任何帮助将不胜感激!


修改:

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

4 个答案:

答案 0 :(得分:2)

由于您具有控制片段膨胀的活动,因此您可以手动控制要膨胀的片段的生命周期。通过调用以下方法,您可以控制准备使用全局数据的片段。在这一点上,您将需要一些如何将数据传递回Mainactivity来确定哪个片段处于活动状态,因为您询问如何同时增加两个将共享一个对象的片段。更好的方法是让MainActivity实现具有特定类的FragmentA和FragmentB-detail来执行Stuff这样的工作,您必须像对待Tablet一样对待您的应用并确定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();
        }
    }

答案 1 :(得分:2)

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()
    }
}

另一个优点是,您也可以等量实现状态更改。

答案 2 :(得分:2)

也许您可以将一些与初始化相关的代码移到假定为onStart()onCreateView()方法片段的中立状态。根据{{​​3}},这是应该进行初始化的地方。

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

答案 3 :(得分:0)

我的情况有些不同,如果有人遇到相同的问题,我想分享一下。

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

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

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

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