片段中的视图绑定会导致KotlinNullPointerException在生命周期范围的协程中运行

时间:2020-10-12 08:04:57

标签: android kotlin kotlin-coroutines android-viewbinding

我正在像这样设置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来了解生命周期的目的是什么?

3 个答案:

答案 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 的绑定对象。