Kotlin Flow异常处理扩展函数如何添加跨页面可调用的重试回调?
Kotlin Flow 错误重试实现方案
完全可以实现需求,推荐用「重试事件触发器 + Flow内置retryWhen操作符」的方案,不需要额外给已失败的Flow绑定回调,具体实现逻辑如下:
核心思路
- 原生
catch操作符仅能捕获上游异常并终止流,无法重启上游执行,因此配合retryWhen操作符实现流的重试逻辑 - 每个流实例分配唯一ID,通过事件总线匹配错误事件和重试事件,实现跨页面/全局触发重试
- 冷流特性保证重试时会重新执行上游逻辑,符合重新发起网络请求的需求
实现代码
1. 定义事件结构与全局事件总线
import kotlinx.coroutines.flow.MutableSharedFlow import java.util.UUID // 错误上报事件,携带流唯一标识、重试权限、异常本身 data class FlowErrorEvent( val requestId: String, val supportRetry: Boolean, val throwable: Throwable ) // 重试触发事件,对应要重试的流ID data class FlowRetryEvent(val requestId: String) // 全局事件总线,配置缓冲区防止事件丢失 object FlowEventBus { val errorEvents = MutableSharedFlow<FlowErrorEvent>(extraBufferCapacity = 16) val retryEvents = MutableSharedFlow<FlowRetryEvent>(extraBufferCapacity = 16) suspend fun postError(event: FlowErrorEvent) = errorEvents.emit(event) suspend fun postRetry(event: FlowRetryEvent) = retryEvents.emit(event) }
2. 改造Flow错误处理扩展函数
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.retryWhen import retrofit2.HttpException fun <T> Flow<T>.handleErrors( showError: Boolean = false, retry: Boolean = false, navigateBack: Boolean = true, maxRetryCount: Int = 3 // 可选:限制最大重试次数 ): Flow<T> { // 为每个流实例生成唯一ID,用来匹配重试事件 val requestId = UUID.randomUUID().toString() var retryCount = 0 // 重试计数器 return this .retryWhen { throwable, _ -> // 仅满足重试条件时监听重试事件 if (retry && throwable is HttpException && retryCount < maxRetryCount) { // 收到对应ID的重试事件后返回true,触发上游流重新订阅执行 retryEvents.first { it.requestId == requestId } retryCount++ true } else { // 不满足重试条件则交给下游catch处理 false } } .catch { throwable -> if (showError) { // 上报错误事件给Base层弹框 FlowEventBus.postError( FlowErrorEvent( requestId = requestId, supportRetry = retry && retryCount < maxRetryCount, throwable = throwable ) ) } // 原有其他逻辑:页面返回、普通提示等 if (navigateBack) { // 你的页面返回逻辑 } // 可根据需求选择是否重新抛出异常 throw throwable } }
3. Base层接收事件弹框与触发重试
// BaseActivity中监听错误事件弹框 lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { FlowEventBus.errorEvents.collect { event -> AlertDialog.Builder(this@BaseActivity) .setMessage(getErrorMessage(event.throwable)) .apply { if (event.supportRetry) { setPositiveButton("重试") { _, _ -> lifecycleScope.launch { // 发送对应ID的重试事件,触发流重启 FlowEventBus.postRetry(FlowRetryEvent(event.requestId)) } } } setNegativeButton("取消") { _, _ -> // 取消逻辑,需要返回的话直接finish } } .setCancelable(false) .show() } } }
注意事项
- 如果不需要全局跨页面触发重试,可以把事件总线替换为ViewModel内的
SharedFlow,作用域更安全 - 页面销毁时只要取消对应Flow的订阅,就不会触发无效重试
- 重试逻辑仅对冷流生效,热流需要根据实际场景调整上游重启逻辑
内容的提问来源于stack exchange,提问作者George
相关产品推荐
相关产品推荐

