Kotlin调用ViewModel方法提示lateinit属性_mails未初始化该如何解决
报错根因
viewModelScope.launch(Dispatchers.IO)启动的是异步任务,不会阻塞ViewModel初始化的主线程。数据库查询属于耗时操作,执行时序会晚于Fragment的onStart生命周期回调- 你在
onStart中同步调用getAllConnections()时,IO线程的_mails赋值操作还未完成,lateinit修饰的变量还未初始化,就抛出了对应异常
解决方案(推荐使用响应式架构实现)
Android MVVM架构下推荐使用StateFlow实现数据的响应式通知,无需手动控制异步任务时序,数据加载完成后自动更新UI:
1. 修改ConnectionViewModel代码
class ConnectionViewModel( val database: ConnectionDataBaseDao, application: Application) : AndroidViewModel(application){ // 私有可变状态流,初始值设置为空列表 private val _mails = MutableStateFlow<List<Connection>>(emptyList()) // 对外暴露不可变状态流,避免外部随意修改数据 val mails = _mails.asStateFlow() init { viewModelScope.launch(Dispatchers.IO) { // 数据库查询完成后更新状态流的值 _mails.value = database.getAllConnections() } } }
2. 修改ConnectionFragment代码
移除onStart中的同步调用逻辑,改为监听StateFlow的数值变化,数据返回后再设置适配器:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) // 绑定生命周期监听数据变化 lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { viewModel.mails.collect { mailList -> val mails = mailList.map { it.mailId } val adapter = ArrayAdapter<String>(requireContext(), android.R.layout.simple_list_item_1, mails) binding.mail.setAdapter(adapter) } } } }
补充说明
如果你不想使用响应式方案,也可以将_mails的lateinit修饰符移除,直接初始化赋值为emptyList(),但这种方式你仍然需要额外处理数据加载完成后刷新UI的逻辑,否则永远只会显示空列表,因此不推荐使用。
内容的提问来源于stack exchange,提问作者Ayman Mn
相关产品推荐
相关产品推荐

