当 Fragment 重新加载时,ViewModel 范围内的 Fragment 不会被破坏

ViewModel scoped to Fragment does not get destroyed when Fragment gets reloaded

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

ViewModel 当前在 Fragment 的 onViewCreated() 方法中使用 'this' 关键字限定为 fragment:

    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 的范围限定到 Fragment 的 ViewLifecycleOwner() 是否是解决问题的好方法?

所以改变这个:

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

对此:

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

根据 Navigate to a destination documentation:

Android maintains a back stack that contains the destinations you've visited. The first destination of your app is placed on the stack when the user opens the app. Each call to the navigate() method puts another destination on top of the stack.

因此,当您调用 navController.navigate(R.id.addInventoryFragment, null); 时,您正在将添加清单片段的一个全新实例添加到返回堆栈,这意味着您的返回堆栈现在有两个副本。如果你再次调用 navigate(),你将得到三份,然后是四份,等等。

您可以 use popUpTo or, if you know you're always replacing the Fragment with itself, use setLaunchSingleTop(true):

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

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