为啥不在 Android Fragment 数据绑定中使用 lateinit 修饰符?
Posted
技术标签:
【中文标题】为啥不在 Android Fragment 数据绑定中使用 lateinit 修饰符?【英文标题】:Why not use lateinit modifier in Andrioid Fragment view-binding?为什么不在 Android Fragment 数据绑定中使用 lateinit 修饰符? 【发布时间】:2022-01-01 01:20:08 【问题描述】:在android documentation 中我们有没有lateinit
的数据绑定示例:
private var _binding: ResultProfileBinding? = null
// This property is only valid between onCreateView and
// onDestroyView.
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View?
_binding = ResultProfileBinding.inflate(inflater, container, false)
val view = binding.root
return view
override fun onDestroyView()
super.onDestroyView()
_binding = null
为什么我们不使用lateinit
,就像我们在活动中使用它一样:
private lateinit var binding: ResultProfileBinding? = null
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View?
binding = ResultProfileBinding.inflate(inflater, container, false)
return binding.root
我怀疑它存在内存泄漏问题。能解释一下吗?
【问题讨论】:
【参考方案1】:我找到了一个很好的解释here。
解释中的片段:
碎片中的泄漏是如何发生的?首先,我们需要从回顾开始 片段的重要细微差别。它们有两个不同的生命周期:
它自己的生命周期(onCreate
和onDestroy
) 这是视图的生命周期(onCreateView
和onDestroyView
)单个生命周期有两个生命周期 屏幕可能有问题。它们在不同的地方被创建和销毁 例如,当将片段放在后台堆栈时。 具体来说,在调用
onDestroyView
之后保留视图将 泄漏。当片段在后堆栈上时会发生这种情况,尽管 它的视图被破坏了,片段本身没有。垃圾 收集器无法清除对这些视图的引用。
还有一个来自this Stack Overflow 的 sn-p 答案:
您必须取消对
onDestroyView
中视图的引用 这是片段不再使用视图的标志 系统,如果它不适合您,它可以安全地被垃圾收集 继续参考View
。
【讨论】:
【参考方案2】:看看这个例子
// here a firestore database uses callback to be executed when the document recieved
db.collection("cities").document("SF").get()
.addOnSuccessListener document ->
if (document != null)
binding.textView.text = document.data.toString()
else
Log.d(TAG, "No such document")
如果用户在收到文档之前打开片段并关闭它(这意味着片段不再被使用,如果变量为空或不再使用,则垃圾收集器应该清除它的所有变量)
现在让我们讨论一下lateinit
的场景
private lateinit var binding: ResultProfileBinding
车库收集器不会清除binding
,因为它仍在回调中使用,并且片段将保留在内存中导致内存泄漏,然后执行回调并设置用户不知道的文本那是因为他留下了碎片
想象一下,如果用户多次执行此场景!
那么可空绑定呢?
private var _binding: ResultProfileBinding? = null
private val binding get() = _binding!!
您在 onDestroyView
中将其设置为 null,以便可以对 binding
和片段进行垃圾收集(无内存泄漏)
但是当回调执行时会发生什么?
你会得到一个NullPointerException
,所以要注意这一点
无论用户打开和关闭片段多少次,它都会被垃圾回收
尝试使用此代码,您可以使用Android Studio Profiler 来查看设备内存和/或leakCanary 来获取有关应用程序内存泄漏的通知
【讨论】:
【参考方案3】:片段中的binding
可能会导致内存泄漏,如果它们在onDestroyView
中未设置为空。这就是为什么我们在onDestroyView
中将_binding
设置为null
在使用lateinit
时,我们不能将lateinit
属性分配给null
。因此,如果我们使用lateinit binding
,我们将无法在onDestroyView
中将其设置为空,这将导致memory leak
。这就是为什么 android 文档建议使用 nullable
变量的原因。
对于activities
,我们不需要将binding
分配给null
,因为它们不会导致内存泄漏,我们可以为binding
使用lateinit
属性。
【讨论】:
但是为什么,你能用一个例子来解释(场景)。这个答案可以在internet找到。 您所说的一切与文档所说的相同。您能否提供一种可能发生内存泄漏的情况? @praveen 片段比他们的观点更长寿,如果观点被破坏,那么它可以持有那个绑定引用。 这不是内存泄漏的传统定义。这更像是临时内存泄漏,其中 Fragment 实例挂在对操作系统想要释放的过时视图的引用上,因为 Fragment 在后台堆栈上。以上是关于为啥不在 Android Fragment 数据绑定中使用 lateinit 修饰符?的主要内容,如果未能解决你的问题,请参考以下文章
Android:何时/为啥我应该使用 FrameLayout 而不是 Fragment?
为啥android fragment 不调用 oncreateview方法
为啥此错误显示“不兼容的类型”。 “必需:android.support.v4.app.Fragment”?