如何等待viewModelScope.launch执行完成后再进行页面导航?
解决Room数据库保存未完成就跳转导致数据丢失的问题
问题根源
你当前的代码中,deleteAllPlayerRecords()、savePlayersRecord()、saveScoreCardRecord()内部通过viewModelScope.launch启动异步协程执行数据库操作,但这些协程不会阻塞onSaveSelection()的执行。点击按钮后,onSaveSelection()立刻返回并触发页面跳转,当前页面的ViewModel可能被销毁,viewModelScope会取消所有未完成的协程,导致数据库操作中断,数据保存不完整。
解决方案
核心思路是等待所有数据库操作完成后再执行页面跳转,具体步骤如下:
1. 将ViewModel中的数据库操作改为挂起函数
Room的DAO方法原生支持挂起(用suspend修饰),调用挂起方法会自动在后台线程执行,并阻塞当前协程直到操作完成。需要把原来启动协程的方法改成直接调用挂起DAO方法的挂起函数:
// ViewModel中的代码修改 suspend fun deleteAllPlayerRecords() { // 直接调用DAO的挂起删除方法 playerDao.deleteAll() } suspend fun savePlayersRecord() { // 循环保存玩家数据,调用DAO的挂起插入方法 players.forEach { player -> playerDao.insert(player) } } suspend fun saveScoreCardRecord() { // 调用DAO的挂起插入方法保存计分卡 scoreCardDao.insert(scoreCard) } // 统一执行所有保存操作的挂起函数 suspend fun saveAllRecords() { // 顺序执行三个操作,全部完成后才会返回 deleteAllPlayerRecords() savePlayersRecord() saveScoreCardRecord() }
2. 在UI点击事件中等待保存完成再跳转
在Compose中,点击事件是普通函数无法直接挂起,需要用rememberCoroutineScope创建与组件生命周期绑定的协程作用域,在协程内先执行保存操作,完成后再跳转:
// UI层的点击事件修改 val coroutineScope = rememberCoroutineScope() onClick = { coroutineScope.launch { try { // 等待所有保存操作完成 viewModel.saveAllRecords() // 保存完成后再跳转页面 navHostController.navigate(Screen.ScoreCard.route) } catch (e: Exception) { // 处理保存失败的情况,比如打印日志或提示用户 Log.e("SaveError", "保存数据失败", e) // 可添加Snackbar/Toast提示用户保存失败 } } }
关键说明
- 挂起函数确保数据库操作顺序执行且全部完成,避免异步协程被ViewModel销毁中断。
rememberCoroutineScope的作用域与当前Compose组件绑定,会在组件销毁时取消协程,但由于我们先等待保存完成再跳转,此时操作已执行完毕,不会出现数据丢失。- 必须确保Room DAO中的对应方法都用
suspend修饰,否则无法实现挂起等待。
内容的提问来源于stack exchange,提问作者RugbyGuy
相关产品推荐
相关产品推荐

