切换请求状态时ViewBinding出现NullPointerException问题排查
问题分析:Fragment返回后空状态页面崩溃问题
问题场景
我正在实现应用的空状态页面,但第二次返回至Fragment时应用崩溃。首次启动应用时视图可正常创建,但当完成主Fragment→注册文档Fragment(为RecyclerView列表创建新项)→主Fragment的流程后,应用在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 -> { } } } }
ResourceState密封类
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>() }
BaseFragment抽象类
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 }
报错日志
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)
崩溃原因
核心原因是协程未绑定视图生命周期,视图销毁后仍尝试访问已置空的Binding:
- 当从主Fragment跳转到注册文档Fragment时,主Fragment会执行
onDestroyView,此时_binding被置为null。 - 但
configDataCollector()中用lifecycleScope.launch启动的协程,默认绑定的是Fragment的整体生命周期(从onCreate到onDestroy),并不会在onDestroyView时自动取消。 - 当在注册文档Fragment添加数据后,ViewModel的
list数据流会发送更新事件,之前启动的协程仍然会执行collectLatest中的逻辑,此时访问binding会通过_binding!!强制解包null值,直接抛出NullPointerException。 - 首次启动正常是因为视图尚未销毁,
_binding不为null,协程访问Binding无问题。
修复建议
将协程绑定到视图生命周期,确保视图销毁时协程自动取消:
private fun configDataCollector() = viewLifecycleOwner.lifecycleScope.launch { viewModel.list.collectLatest { result-> // 原有逻辑不变 } }
内容的提问来源于stack exchange,提问作者Tods
相关产品推荐
相关产品推荐

