我正在像这样设置suggested in Google docs这样的片段:
private var _binding: MyBinding? = null
private val binding get() = _binding!!
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
_binding = MyBinding.inflate(inflater, container, false)
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
现在,我正在呼叫一个协程,据我了解,该范围应限于此片段的生命周期。它拥有更长的网络通话时间,然后成功:
lifecycleScope.launch(Dispatchers.Main) {
when (myViewModel.loadFromNetwork(url)) {
true -> responseSuccess()
false -> responseFailure()
}
}
private suspend fun responseSuccess() {
binding.stateSuccess.visibility = View.VISIBLE
// ...
}
现在,当我在loadFromNetwork
仍在加载时按Android系统后退按钮时,该片段将被销毁并调用onDestroyView()
。这样binding
现在是null
。我收到kotlin.KotlinNullPointerException
。尽管我认为responseSuccess()
专门用于这类情况,但我仍然不太明白为什么lifecycleScope
仍在执行。根据{{3}}:
为每个Lifecycle对象定义一个LifecycleScope。销毁生命周期后,将取消在此范围内启动的所有协程。
我知道可以通过一些更改和一些手动的空检查来解决此代码,但是我想了解如何在不使用样板的情况下以预期的方式解决此问题。使用lifecycleScope来了解生命周期的目的是什么?
答案 0 :(得分:1)
协程取消是 cooperative。这意味着检查取消是协程本身的责任。协程库中的大多数(或可能是全部)挂起操作检查取消,但如果您不调用其中任何一个,则需要使您的代码可取消,如here 所述。
在协程中处理视图的更好选择是使用 lifecycle extensions,当生命周期状态不是必需状态时,它会自动挂起/取消协程。
另请注意,取消只是常规的 CancellationException
,因此请检查您是否不小心发现了它。
答案 1 :(得分:0)
您使用的lifecycleScope与片段视图的生命周期不同。因此,您必须使用其他范围viewLifecycleOwner.lifecycleScope.launch {}
。顺便说一句,指向Google文档的链接就是这样:)
答案 2 :(得分:-1)
好吧,这可能不是一种非常干净的处理方式,但我建议您自己取消 onDestroyView
中的工作
在类级别定义作业,例如
lateinit var job:Job
然后像这样分配
job = lifecycleScope.launch(Dispatchers.Main) {
when (myViewModel.loadFromNetwork(url)) {
true -> responseSuccess()
false -> responseFailure()
}
并在将 null 分配给 _binding 之前在 onDestroView 方法中取消它。
override fun onDestroyView() {
super.onDestroyView()
job.cancel()
_binding = null
}
你得到 NULL POINTER EXCEPTION 的原因是片段有两个生命周期。 1)生命周期和视图生命周期。 _binding 在 onDestroyView 中被赋值为 null 但片段生命周期仍然存在,所以协程的工作正在做它的工作,当网络响应到达时,它运行启动块并想要访问到那时为 null 的绑定对象。