重新加载Fragment时不会破坏范围为Fragment的ViewModel

时间:2019-11-25 17:12:25

标签: android android-fragments android-lifecycle

我的应用允许用户提交表单(Androidx Fragment)以保存数据。提交表单约30次后,应用程序内存不足并崩溃。内存转储的比较表明,问题的根源之一是提交表单时,与表单片段相关联的ViewModel并未被破坏。

使用片段的onViewCreated()方法中的'this'关键字,当前将ViewModel的作用域限定为片段:

    vm = new ViewModelProvider(this).get(AddInventoryVM.class);

当用户提交表单时,通过使用“ Android体系结构组件”中的“ Android导航”来导航到同一片段,从而重新加载该片段。

    navController.navigate(R.id.addInventoryFragment, null);

在此过渡期间,不会对ViewModel的实例进行垃圾回收。 onViewCreated()被调用并创建ViewModel的新实例-多次发生时会导致内存问题。不会调用'onDestroy()',但是在过渡期间会调用onDestroyView()

原因可能是Fragment实例在过渡期间未销毁(导致未对ViewModel进行垃圾回收)-仅销毁了Fragment的视图。但是,如果是这种情况,则不会增加任何东西-Android不会在过渡时重用现有的ViewModel而不是创建新的ViewModel吗?

尽管存在上述差异,将ViewModel范围限定为Fragment的ViewLifecycleOwner()是解决此问题的好方法吗?

因此要更改此内容:

    vm = new ViewModelProvider(this).get(AddInventoryVM.class);

对此:

    vm = new ViewModelProvider(getViewLifecycleOwner()).get(AddInventoryVM.class);

1 个答案:

答案 0 :(得分:1)

按照Navigate to a destination documentation

  

Android会维护一个包含您访问过的目的地的后台堆栈。当用户打开应用程序时,您的应用程序的第一个目标位于堆栈上。每次对navigate()方法的调用都会在栈顶放置另一个目标。

因此,当您调用navController.navigate(R.id.addInventoryFragment, null);时,您是将添加库存片段的全新实例添加到后堆栈中,这意味着您的后堆栈现在有两个副本。如果您再次致电navigate(),则将有3个副本,然后有4个副本,等等。

您可以use popUpTo,或者,如果您知道自己总是用它自己替换片段,请使用setLaunchSingleTop(true)

NavOptions options = new NavOptions.Builder()
    .setLaunchSingleTop(true)
    .build();
navController.navigate(R.id.addInventoryFragment, null, options);

(如果您使用的是<acction> in Navigation XML,则可以使用app:launchSingleTop="true"做同样的事情)