如何在Kotlin Flow的retry与catch中向ViewModel发射资源?
Kotlin Flow:在retry和catch环节发射自定义Resource状态
问题描述
我有如下Kotlin Flow流程结构,核心逻辑在use_case中实现,会向viewmodel发射对应Resource值:
import kotlinx.coroutines.* import kotlinx.coroutines.flow.* sealed class Resource<out T:Any>{ data class Success<out T:Any> (val data:T):Resource<T>() data class Error(val exception: Exception):Resource<Nothing>() data class Loading(val message:String):Resource<Nothing>() } fun myUseCase() = flowOf( Resource.Loading("Loading..."), Resource.Loading("Loading..."), Resource.Error(Exception("Error_Occured"))) .onEach { when (it) { is Resource.Error -> { throw it.exception } is Resource.Loading -> {} is Resource.Success -> {} } }.retry(1, { println("retrying...") true }) .catch { println("catch error... $it") } fun myViewModel() = runBlocking<Unit> { myUseCase() .collect { println("Result $it") } } fun main() = runBlocking<Unit> { myViewModel() }
现在我想在retry和catch环节也向ViewModel发射对应的Resource状态:
- 重试时发射
Resource.Loading("retrying...") - 最终捕获异常时发射
Resource.Error(Exception("finished with error"))
我已经知道在catch里可以通过emit实现,但不清楚retry环节怎么处理,请问是否可以实现?
补充:我发现在catch中可以通过如下方式实现发射,但不清楚retry中如何实现:
.catch { emit(Resource.Error(Exception("finished with error"))) }
解决方案
1. catch环节的实现
直接在catch块中调用emit即可发射自定义错误状态,这是Flow的catch操作符原生支持的特性,你已经找对了方法:
.catch { emit(Resource.Error(Exception("finished with error"))) println("catch error... $it") }
2. retry环节的实现:改用retryWhen操作符
标准retry操作符的判断函数无法直接发射数据,需要替换为更灵活的retryWhen。它的处理块是FlowCollector的扩展函数,允许你在判断是否重试的同时,发射自定义的Resource状态。
修改后的完整myUseCase代码如下:
fun myUseCase() = flowOf( Resource.Loading("Loading..."), Resource.Loading("Loading..."), Resource.Error(Exception("Error_Occured")) ) .onEach { when (it) { is Resource.Error -> throw it.exception is Resource.Loading -> {} is Resource.Success -> {} } } // 替换原retry为retryWhen .retryWhen { cause, attempt -> if (attempt < 1) { // 控制最多重试1次 emit(Resource.Loading("retrying...")) // 发射重试状态到ViewModel println("retrying...") true // 返回true表示允许重试 } else { false // 返回false停止重试 } } .catch { emit(Resource.Error(Exception("finished with error"))) println("catch error... $it") }
运行效果
执行main函数后,控制台输出如下:
Result Resource.Loading(message=Loading...) Result Resource.Loading(message=Loading...) retrying... Result Resource.Loading(message=retrying...) Result Resource.Loading(message=Loading...) Result Resource.Loading(message=Loading...) catch error... java.lang.Exception: Error_Occured Result Resource.Error(exception=java.lang.Exception: finished with error)
可以看到,重试时成功发射了Loading("retrying..."),最终异常时也发射了自定义的Error状态,完全符合需求。
内容的提问来源于stack exchange,提问作者Metropol_Tilkisi
相关产品推荐
相关产品推荐

