You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 19:24:58