遵循Google Jetpack应用架构指南开发Android应用时,调用Suspend函数报错的解决方法
解决Suspend函数调用报错的问题
嘿,这个问题我之前也碰到过,咱们一步步来搞定它!
为什么会报错?
你现在的代码里,CategoriesRepository.getCategories()是一个suspend函数,但你直接在ViewModel的属性初始化里调用它并转成LiveData——这里的调用不在任何协程或者另一个suspend函数上下文里,所以编译器会抛出那个错误。虽然官方指南里会用到suspend函数,但那通常是在协程作用域内调用的场景,和你当前的调用方式不一样。
推荐修复方案(贴合Jetpack架构最佳实践)
咱们调整一下Repository的实现,让getCategories()直接返回Flow,同时用Flow的onStart操作符触发数据刷新。这样既保留了Room Flow自动响应数据库变化的特性,又能正确调用suspend的刷新方法:
第一步:修改Repository代码
class CategoriesRepository @Inject constructor(val categoryDao: CategoryDao) { // 去掉suspend修饰符,直接返回Flow fun getCategories(): Flow<List<Category>> { return categoryDao.getAll() // 在Flow开始收集数据时,触发远程数据刷新 .onStart { refreshCategories() } } private suspend fun refreshCategories() { val response = ApiInterface.create().getCategories() categoryDao.insertAll(response.data!!) } }
第二步:ViewModel代码保持不变
@HiltViewModel class CategoriesViewModel @Inject constructor(categoriesRepository:CategoriesRepository) : ViewModel() { val categories : LiveData<List<Category>> = categoriesRepository.getCategories().asLiveData() }
方案原理
onStart是Flow的操作符,它会在Flow被收集(也就是ViewModel里调用asLiveData()后开始观察数据)时执行传入的代码块,而这个代码块允许调用suspend函数。- Room返回的Flow是冷流,只有被收集时才会从数据库拉取数据,结合
onStart刚好能先完成远程数据刷新、写入数据库,再获取最新的本地数据;后续数据库有更新时,Flow还会自动推送新数据到LiveData,完美契合响应式架构的要求。
另一种可选方案(保留suspend函数的场景)
如果坚持要让getCategories()作为suspend函数,那需要在ViewModel的协程作用域内调用它,再把结果赋值给MutableLiveData——但这种方式会失去Flow自动响应数据库变化的优势:
@HiltViewModel class CategoriesViewModel @Inject constructor(categoriesRepository:CategoriesRepository) : ViewModel() { private val _categories = MutableLiveData<List<Category>>() val categories: LiveData<List<Category>> = _categories init { viewModelScope.launch { val categoryList = categoriesRepository.getCategories() _categories.value = categoryList } } }
还是更推荐第一种方案,它更贴合Room+Flow的设计理念,能减少手动维护数据更新的工作量。
内容的提问来源于stack exchange,提问作者nicat mahirson
相关产品推荐
相关产品推荐

