Android 在片段中保留对视图的引用会导致内存泄漏吗?
有人告诉我以下内容,但我有点困惑。 请问,您能确认还是质疑它? (通过Android 在片段中保留对视图的引用会导致内存泄漏吗?,android,android-fragments,Android,Android Fragments,有人告诉我以下内容,但我有点困惑。 请问,您能确认还是质疑它? (通过setRetainInstance() 目前,在片段中初始化视图是一种常见做法,如下所示: private lateinit var myTextView: TextView fun onViewCreated(view: View, bundle: Bundle) { ... myTextView = view.findViewById(R.id.myTextViewId) ...
setRetainInstance()
目前,在片段中初始化视图是一种常见做法,如下所示:
private lateinit var myTextView: TextView
fun onViewCreated(view: View, bundle: Bundle) {
...
myTextView = view.findViewById(R.id.myTextViewId)
...
}
我们永远不会使这个属性无效。虽然这是一种常见的做法,但它会导致内存泄漏
这方面的背景:
比如说,FragmentA
引用了它的视图的子视图,作为一个实例字段。
FragmentManager使用特定的FragmentTransaction执行从片段A到B的导航。根据事务的类型,管理器可能只想终止视图
,但仍然保留FragmentA
的实例(请参见下面的生命周期部分,其中说明“片段从后堆栈返回布局”)。当用户从FragmentB
导航回FragmentA
时,FragmentA
的前一个实例将被带到前面,但会创建一个新的视图
问题是,如果我们在lateinit属性中将实例保留在视图中,并且从未清除对它的引用,则视图无法完全销毁,从而导致内存泄漏。有一个名为onDestroyView
的片段生命周期方法,您应该覆盖该方法以释放对视图的任何引用
通常,如果您的片段
被永久添加到活动
中,并且它不会被删除,则只应使用lateinit var
查看引用
通过自动清除onDestroyView中的视图缓存已经解决了这个问题,我不认为有直接的官方答案,但答案在文档中,可以很容易地证明。如果您检查并描述方法,它会说:
调用onDestroyView
后,将分离布局,下次碎片附着时,将重新创建视图
片段实际上可以从后堆栈返回,在本例中调用onCreateView
这基本上意味着,如果在片段位于后堆栈中时在片段实例中保留任何视图引用,则可以防止这些视图被垃圾收集
如果您仍然不确定,可以查看AOSP的源代码。例如,在onDestroyView()
中将所有视图引用都设置为null
此外,很容易证明存在内存泄漏。只需使用图像视图创建一个片段,该视图将显示完整大小的图像。在片段中保留一个引用,并使用片段事务导航到另一个片段。然后点击强制垃圾收集
按钮,使用该工具创建一个堆转储,您将看到片段实际上持有图像视图和图像本身,阻止它们被收集。如果重复相同的步骤,但在onDestroyView()
中将引用设置为null,您将看到片段的保留大小要小得多,并且堆中不再存在ImageView
实例。Edit2
间接官方信息:
- 和的方法表明,如果没有明确说明,则使用保存状态将
片段添加到backstack
- 方法表明,
Fragment
可以在方向更改和其他活动
娱乐之间保存其状态-这意味着FragmentManager
相对独立于活动
的生命周期存在,即使活动
被破坏,也可以存储Fragment
状态。
- 描述…的一句小评论
在内部,在保存视图的状态之后,但在将其从其父视图中删除之前,会调用它
它指示保存片段
状态的确切时间
所有这些点几乎明确地组合在一起表示存在片段
视图的状态,并且它存储在导航事件之间的内存中
编辑2结束
第一个问题是你的问题
你说你提供的代码是在片段中初始化视图的一种常见做法。这根本不是一种常见做法。这是谷歌只在其示例和示例中使用的一种陈旧过时的方式。虽然对于示例来说已经足够了,但对生产来说并不好
- 谷歌目前针对Kotlin的官方标准之一是通过初始化片段或活动中的视图。谷歌甚至在其现代样本和示例中使用这种方法。有一种称为
clearFindViewByIdCache()
的方法,可以在需要时消除所有强合成引用(最常见于onDestroyView
)
- 第二个标准是通过
、布局xml文件中的
标记和代码中的ViewModel
使用Android数据绑定。它既适用于Kotlin,也适用于Java,非常简单明了。这样做的原因之一是在使用旧的“标准方式”时消除内存泄漏,使其易于保留配置更改的状态,并统一现代UI层实现的方法。要完全消除可能的内存泄漏,您必须在onDestroyView
中取消绑定。如果实现良好,它可以处理所有现成的内容,包括内存泄漏(没有),在配置更改时保留视图状态,通过LivaData
使用网络或数据库中的相关数据更新UI,与UI的一般通信,处理功能等。目前,它与JetPack的其他功能一起是
- 还有第三种半官方的方法-使用。如果实现得好,它也能够处理