kotlin 更改请求状态时在绑定时接收NPE

i2byvkas  于 2023-01-05  发布在  Kotlin
关注(0)|答案(1)|浏览(88)

我正在实现一个应用程序的空状态屏幕,但它在第二次返回片段后崩溃。
当应用程序第一次启动时,它正确地创建了我的视图,但当我向以前的列表添加一些东西时(主片段-〉注册文档片段(为我的回收列表创建新项目)-〉主片段),它在下面的行中崩溃:

binding.imageEmptyState.hide()

从以下函数:

private fun configDataCollector() = lifecycleScope.launch {
    viewModel.list.collectLatest { result->
        when(result) {
            is ResourceState.Success -> {
                binding.imageEmptyState.hide()
                binding.textEmptyState.hide()
                result.data?.let { values ->
                    adapterDoc.docs = values.toList()
                }
            }
            is ResourceState.Empty -> {
                binding.imageEmptyState.show()
                binding.textEmptyState.show()
            }
            else -> { }
        }
    }
}

收货:

2023-01-04 11:55:35.905 14014-14014/com.tods.docreminder E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.tods.docreminder, PID: 14014
java.lang.NullPointerException
    at com.tods.docreminder.feature.doc.presentation.util.BaseFragment.getBinding(BaseFragment.kt:13)
    at com.tods.docreminder.feature.doc.presentation.doc.MainFragment.access$getBinding(MainFragment.kt:34)
    at com.tods.docreminder.feature.doc.presentation.doc.MainFragment$configDataCollector$1$1.invokeSuspend(MainFragment.kt:255)

我的资源状态类:

sealed class ResourceState<T>(
    val data: T? = null,
    val message: String? = null
) {
    class Success<T>(data: T?): ResourceState<T>(data)
    class Error<T>(message: String?, data: T? = null): ResourceState<T>(data, message)
    class Loading<T>: ResourceState<T>()
    class Empty<T>: ResourceState<T>()
}

碱基片段:

abstract class BaseFragment<VB: ViewBinding, VM: ViewModel>: Fragment() {
    private var _binding: VB? = null
    protected val binding get() = _binding!!
    protected abstract val viewModel: VM

    override fun onCreateView
                (inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        _binding = recoverViewBinding(inflater, container)
        return binding.root
    }

    override fun onDestroyView() {
        super.onDestroyView()
        _binding = null
    }

    protected abstract fun recoverViewBinding(inflater: LayoutInflater, container: ViewGroup?): VB
}

为什么会发生在这种情况下?
谢谢你的帮助,如果需要任何其他代码,我会尽快编辑的问题。

0md85ypi

0md85ypi1#

Fragment示例的生命周期比其视图的生命周期长。如下所示,这些函数表示开始和停止的生命周期。在Fragment的生命周期仍在运行时,可以多次创建和销毁视图。

Fragment
  -onCreate
    -onCreateView   ** These two lines can be called multiple times
    -onDestroyView  ** inside the Fragment's outer lifecycle.
  -onDestroy

因此,如果你在一个协程中引用binding,而这个协程位于Fragment的lifecycleScope上,而不是位于viewLifecyclelifecycleScope上,那么你的协程就有可能在binding为空时尝试使用它,此时:

Fragment
  -onCreate
    -onCreateView
    -onDestroyView
    **Accessing binding here will throw an NPE because it is null after onDestroyView
  -onDestroy

如果你使用viewlifecycle.lifecycleScope作为你的协程,当视图被销毁时,它会自动被取消,所以在Fragment的生命周期中,它永远不会尝试在这些无效的时间使用binding
这是一个观点的问题,但我想指出的是,我认为BaseFragments引起的问题比它们解决的问题更多。

由于Kotlin具有扩展函数和属性委托,因此您可以获得相同的好处,而不会遇到上面文章中提到的所有问题。

相关问题