有人告诉我以下内容,但我有些困惑。

请问,您可以确认或提出异议吗?

(该片段未通过setRetainInstance()保留

目前,在Fragments中初始化 View 是一种常见的做法,如下所示:

private lateinit var myTextView: TextView

fun onViewCreated(view: View, bundle: Bundle) {

     ...

     myTextView = view.findViewById(R.id.myTextViewId)

     ...

}

然后,我们永远不会使该属性无效。尽管这是一种常见的做法,但它会导致内存泄漏。

背景:

假设FragmentA引用了View的childView作为实例字段。
由片段管理器使用特定的FragmentTransaction执行从片段A到B的导航。根据事务的类型,管理器可能只想杀死View,但仍然保留FragmentA实例(请参见生命周期部分,其中显示“片段从后堆栈返回布局”)。当用户从FragmentB导航回FragmentA时,会将FragmentA的先前实例放到最前面,但是会创建一个新的View

问题是,如果我们在lateinit属性中保留 View 的实例,并且从不清除对它的引用,则该 View 无法完全销毁,从而导致内存泄漏。

最佳答案


关于此问题的正式答案是

this documentation中,Android官员教您如何自己找出内存泄漏,以便他们不必对用户可能执行的每个测试用例进行回答。此外,您可以使用LeakCanary,它在检测内存泄漏方面做得很好。
为了您的方便,我执行了堆分析(与您的用例类似但扩展的版本)。在显示analysis report之前,我想逐步介绍如何根据您的情况来取消/分配内存,

  • 在打开的FragmentA上:这是内容/根View,并且TextView将分配到内存中。
  • 开启导航至FragmentB:onDestroyView() FragmentA将为
    被调用,但是FragmentAView无法销毁,因为TextView对它和FragmentA都有很强的引用
    拥有对TextView的强烈引用。
  • 开从FragmentA导航回FragmentB:ViewTextView的分配将被清除。同时,随着onCreateView()的调用,他们将获得新的分配。
  • 在背面按时从FragmentA按下:新分配将被清除为
    好吧。

  • 回答您的问题:
    在第2步中,我们可以看到内存泄漏,因为View的保留内存没有释放它原本应该的状态。另一方面,从步骤3中我们可以看到,一旦用户返回Fragment,内存就会恢复。因此,我们可以找出这种内存泄漏持续存在,直到FragmentManagerFragment带回为止。
    示例/静态分析
    为了测试您的情况,我创建了一个应用程序。我的应用程序有一个带有ActivityButtonFrameLayour,后者是片段的容器。按下Button将容器替换为FragmentAFragmentA包含一个Button,按会用FragmentB替换该容器。 FragmentB具有一个TextView,它作为实例字段存储在片段中。
    android - 在 fragment 中保留对 View 的引用会导致内存泄漏?-LMLPHP
    This report基于对上述应用程序执行的以下操作(仅考虑我创建的 View ,即ConstraintLayout,Framelayout,Button和TextView),
  • 打开应用程序: Activity 可见
  • 在 Activity 中按下按钮: FragmentA可见
  • 按下FragmentA中的按钮: FragmentB可见,FragmentA onDestroyView()
  • 按下了 Activity 中的按钮:第二实例FragmentA可见,FragmentB onDestroyView()。 (这与上一个示例中的步骤2相同,但FragmentB充当A,而FragmentA的第二个实例充当B)
  • 在第二个实例FragmentA中按下按钮:可见第二个FragmentB实例和第二个FragmentA onDestroyView()实例。
  • 按下后退按钮:可见FragmentA的第二个实例和FragmentB的第二个实例onDetach()
  • 按下后退按钮:可见FragmentB的第一个实例和FragmentA的第二个实例onDetach()
  • 按下后退按钮:可见FragmentA的第一个实例和FragmentB的第一个实例onDetach()
  • 按下后退按钮: FragmentA的第一个实例onDetach()
  • 按下后退按钮:,它关闭了应用程序。

  • 观察
    如果您查看report,则可以在第1步中看到每个 View 都存在,直到关闭该应用程序为止。在第2步中,分配FragmentA的View(即FrameLayout)及其子项,Button,并在第3步中将其清除,这是预期的。在第3步中,分配了FragmentB的View,即FrameLayout及其子TextView,但在第4步中未清除,因此导致内存泄漏,但在再次创建View并分配新创建的View的第7步中清除了。另一方面,在第5步中创建的Views在第6步中被清除,没有导致内存泄漏,因为该片段已分离,并且它们并没有阻止清除该片段。
    结论
    我们观察到,将 View 保存在片段中的泄漏一直持续到用户返回片段为止。当片段被带回时,即调用onCreateView(),泄漏得以恢复。另一方面,当片段在顶部且只能返回时,不会发生泄漏。基于此,我们可以得出以下结论:
  • 当片段中没有向前事务时,将 View 保存为强引用没有错,因为它们将在onDetach()
  • 中清除
  • 如果存在远期交易,我们可以存储 View 的弱引用,以便在onDestroyView()
  • 中清除它们

    P.S. 如果您不了解堆转储,请观看Google I/O 2011: Memory management for Android Apps。另外,this link提供了有关内存泄漏的有值(value)的信息。
    我希望我的回答可以帮助您清除困惑。让我知道您是否仍然感到困惑?

    关于android - 在 fragment 中保留对 View 的引用会导致内存泄漏?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/58080268/

    10-10 19:48