有人告诉我以下,但我有点困惑。
请问,您能确认或提出异议吗?
(不通过 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
:它的内容/根View
和 TextView
会被分配到内存中。FragmentB
: onDestroyView()
的 FragmentA
将会打电话,但是
FragmentA
的 View
不能被破坏,因为TextView
强烈引用它和 FragmentA
强烈引用 TextView
.FragmentA
来自 FragmentB
:以前的View
的分配和 TextView
将被清除。同时,他们将获得新的分配,如 onCreateView()
叫做。FragmentA
的背面新闻:新的分配将被清除为好。
回答你的问题:
在第 2 步中,我们可以看到存在内存泄漏,因为 View 的保留内存没有释放它应该释放的内存。另一方面,从第 3 步我们可以看到,一旦用户返回
Fragment
,内存就被恢复了。 .所以我们可以看出这种内存泄漏一直持续到FragmentManager
带来了Fragment
背部。示例/静态分析
为了测试您的案例,我创建了一个应用程序。我的应用程序有一个
Activity
与 Button
和一个 FrameLayour
这是 fragment 的容器。按 Button
将容器替换为 FragmentA
. FragmentA
包含 Button
,按 将用 FragmentB
替换容器. FragmentB
有一个 TextView
它作为实例字段存储在 fragment 中。This report基于对上述应用程序执行的以下操作(仅考虑我创建的 View ,即 ConstraintLayout、Framelayout、Button 和 TextView),
FragmentA
可见 FragmentB
可见和 FragmentA
onDestroyView()
FragmentA
可见和 FragmentB
onDestroyView()
. (这与前面示例中的步骤 2 相同,除了 FragmentB
充当 A,FragmentA
的第二个实例充当 B) 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 在顶部时不会发生泄漏并且只能返回。基于此,我们可以得出以下结论,
onDetach()
中被清除。 onDestroyView()
中清除它们。 附言如果您不了解堆转储,请观看 Google I/O 2011: Memory management for Android Apps .另外,this link提供有关内存泄漏的宝贵信息。
我希望我的回答能帮助你清除你的困惑。让我知道你是否还有困惑?
关于android - 在 Fragment 中保留对 View 的引用会导致内存泄漏?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58080268/