android - 在 Fragment 中保留对 View 的引用会导致内存泄漏?

标签 android android-fragments

有人告诉我以下,但我有点困惑。

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

(不通过 setRetainInstance() 保留 fragment )

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

private lateinit var myTextView: TextView

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

     ...

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

     ...

}

然后我们永远不会取消这个属性。虽然这是一种常见的做法,但它会导致内存泄漏。

背景:

比方说,FragmentA引用了它的 subview View , 作为实例字段。
FragmentManager 使用特定的 FragmentTransaction 执行从 fragment A 到 B 的导航。根据事务的类型,Manager 可能想要终止 View仅但仍然保留 FragmentA 的实例(请参阅下面的生命周期部分,其中说“fragment 从后堆栈返回到布局”)。当用户从 FragmentB 导航回来时至 FragmentA , FragmentA 的前一个实例将被带到前面,但一个新的View将被创建。

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

最佳答案

Please, is there an official answer on the matter?


关于此事的官方答复是,

The Memory Profiler is a component in the Android Profiler that helps you identify memory leaks and memory churn that can lead to stutter, freezes, and even app crashes.


this documentation ,android官方教你如何自己找出内存泄漏,这样他们就不必回答用户可能执行的每一个测试用例。此外您还可以使用 LeakCanary这在检测内存泄漏方面做得很好。
为方便起见,我进行了堆分析(您用例的类似但扩展版本)。在展示之前analysis report我想一步一步地基本概述如何在您的情况下取消/分配内存,
  • 开放中 FragmentA :它的内容/根ViewTextView会被分配到内存中。
  • 在导航到 FragmentB : onDestroyView()FragmentA将会
    打电话,但是 FragmentAView不能被破坏,因为TextView强烈引用它和 FragmentA强烈引用 TextView .
  • 在导航回 FragmentA来自 FragmentB :以前的View的分配和 TextView将被清除。同时,他们将获得新的分配,如 onCreateView()叫做。
  • 来自 FragmentA 的背面新闻:新的分配将被清除为
    好。

  • 回答你的问题:
    在第 2 步中,我们可以看到存在内存泄漏,因为 View 的保留内存没有释放它应该释放的内存。另一方面,从第 3 步我们可以看到,一旦用户返回 Fragment,内存就被恢复了。 .所以我们可以看出这种内存泄漏一直持续到FragmentManager带来了Fragment背部。
    示例/静态分析
    为了测试您的案例,我创建了一个应用程序。我的应用程序有一个 ActivityButton和一个 FrameLayour这是 fragment 的容器。按 Button将容器替换为 FragmentA . FragmentA包含 Button ,按 将用 FragmentB 替换容器. FragmentB有一个 TextView它作为实例字段存储在 fragment 中。
    enter image description here
    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 中创建的 View 在步骤 6 中刚刚被清除,不会导致内存泄漏,因为 fragment 被分离并且它们没有阻止 fragment 被清除。
    结论
    我们观察到在 fragment 中保存 View 的泄漏持续到用户返回 fragment 。当 fragment 被带回,即 onCreateView() 被调用时,泄漏被恢复。另一方面,当 fragment 在顶部时不会发生泄漏并且只能返回。基于此,我们可以得出以下结论,
  • 当没有来自 fragment 的前向交易时,将 View 保存为强引用并没有错,因为它们将在 onDetach() 中被清除。
  • 如果存在远期交易,我们可以存储 View 的弱引用,以便在 onDestroyView() 中清除它们。

  • 附言如果您不了解堆转储,请观看 Google I/O 2011: Memory management for Android Apps .另外,this link提供有关内存泄漏的宝贵信息。
    我希望我的回答能帮助你清除你的困惑。让我知道你是否还有困惑?

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

    相关文章:

    Android FullScreen 主题降低显示按钮的高度

    android - Fragment 上的手势检测

    android - 新手应该忘记 fragment 吗?

    android - 我如何在 fragment 布局中使用自定义字体?

    android - 如何从 DialogFragment 获取数据到 Fragment?

    android - java.lang.NullPointerException : CameraUpdateFactory is not initialized logcat exception 异常

    android - 什么时候(如果有的话)我应该使用 Bitmap.recycle()?

    Android Studio - Maps.me 设置

    android - 如何在 TextView 中单击一次获得选择光标

    android - Android 中的 TimePickerDialog