有人告诉我以下内容,但我有些困惑。
请问,您可以确认或提出异议吗?
(该片段未通过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
将为被调用,但是
FragmentA
的View
无法销毁,因为TextView
对它和FragmentA
都有很强的引用拥有对
TextView
的强烈引用。FragmentA
导航回FragmentB
:View
和TextView
的分配将被清除。同时,随着onCreateView()
的调用,他们将获得新的分配。FragmentA
按下:新分配将被清除为好吧。
回答您的问题:
在第2步中,我们可以看到内存泄漏,因为View的保留内存没有释放它原本应该的状态。另一方面,从步骤3中我们可以看到,一旦用户返回
Fragment
,内存就会恢复。因此,我们可以找出这种内存泄漏持续存在,直到FragmentManager
将Fragment
带回为止。示例/静态分析
为了测试您的情况,我创建了一个应用程序。我的应用程序有一个带有
Activity
和Button
的FrameLayour
,后者是片段的容器。按下Button
将容器替换为FragmentA
。 FragmentA
包含一个Button
,按会用FragmentB
替换该容器。 FragmentB
具有一个TextView
,它作为实例字段存储在片段中。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步中创建的Views在第6步中被清除,没有导致内存泄漏,因为该片段已分离,并且它们并没有阻止清除该片段。
结论
我们观察到,将 View 保存在片段中的泄漏一直持续到用户返回片段为止。当片段被带回时,即调用onCreateView(),泄漏得以恢复。另一方面,当片段在顶部且只能返回时,不会发生泄漏。基于此,我们可以得出以下结论:
onDetach()
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/