绑定(binding)和_binding有什么区别?

5

我正在尝试理解在片段中使用视图绑定的实现方式,发现与活动不同。

在活动中:

private lateinit var binding: ResultProfileBinding

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    binding = ResultProfileBinding.inflate(layoutInflater)
    val view = binding.root
    setContentView(view)
}

在一个片段中:
private var _binding: ResultProfileBinding? = null
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
}

现在我的问题是,为什么我们在 fragment 中有 binding_binding?这一行代码是做什么的,它的目的是什么?

private val binding get() = _binding!!
2个回答

4
在第二个示例中,_binding 属性是可空的,以允许在它被“初始化”之前存在状态。然后 binding 属性具有 getter 方法,以便于访问,假设支持字段 (_binding) 已被初始化。
你所指的特定行意味着当你尝试访问 binding 时,它将返回 _binding。但是,null 断言运算符(!!)添加了额外的断言,即 _binding 不为空。
实际上,你所做的一切只是创建了 lateinit 属性的模拟,如果你查看 lateinit 声明的反编译字节码,它们是相同的。
然而,正如@Tenfour04指出的那样,这里的微小差异在于第二种方法允许你将支持字段设置回 null,而使用 lateinit 属性则无法做到这一点。当你在 fragment 中使用绑定时,推荐在 onDestroyView 中将绑定设为 null,以避免内存泄漏,所以他们在 fragment 中选择了这种方法。

在什么情况下会适用于方法2? 你能举个例子吗? @Henry Twist - Saraladewi
1
由于当视图被销毁时活动也会被销毁,因此不必担心视图泄漏问题,可以使用更简洁的lateinit。因此,在文档中使用不同策略的原因与活动和片段有关。 - Tenfour04
@Tenfour04 如果我们创建一个getter来访问一个带有!!的可空属性,它是否像使用lateinit一样危险? 在一个项目中,我正在使用lateinit并在一个活动中替换片段,并没有遇到问题。您介意举个例子说明为什么它不安全吗?也许是发出一个请求,在切换片段后尝试更新UI? - JCarlosR
2
lateinit比单独使用!!强制属性更安全,因为您不会意外获得NullPointerException。使用单独的!!强制属性时,必须小心不要在片段销毁后可能被调用的回调中使用它。Google建议使用!!强制属性,以便在视图销毁时将支持绑定属性更改回null。这可以防止在分离片段期间暂时泄漏绑定中的视图。 - Tenfour04
1
实际上,片段处于分离状态的时间非常短 - 不到一秒钟。它要么即将被销毁,要么在屏幕旋转后即将被重用。因此,我认为许多有经验的开发人员会说 !! 强制属性是过度设计。使用 lateinit 更简单,从理论上讲也更安全,尽管如果您在分离状态下访问视图,我会说您很草率。@JCarlosR - Tenfour04
显示剩余2条评论

0
在片段中,View 是在 onCreateView() 方法调用中实例化的。但是,在创建片段时,并不是首先调用 onCreateView() 方法,这意味着在 onCreateView() 方法调用之前,View 可能为空,如果 View 为空,那么 _binding 属性也将为空。为了避免 nullPointerException,我们使用一个变量“binding”来获取 _binding 的最新非空值(!! 检查)。
private val binding get() = _binding!!

这个声明有一个内置的get()方法,每次我们调用binding时都会更新binding的值,以返回当前非空值的_binding。因为_binding!!如果_binding的值为空,将会抛出异常。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接