用通俗易懂 + Android 开发实战的方式,详细讲解 Kotlin Flow 中的 retryWhen 操作符
一、一句话理解 retryWhen
retryWhen是 Flow 的“智能重试”机制:当流抛出异常时,你可以决定“要不要重试”以及“重试几次”。
它就像一个“故障处理员”:
- Flow 执行出错了(比如网络请求失败)
retryWhen问你:“要不要再试一次?”- 你根据错误类型、重试次数等条件,返回
true(重试)或false(放弃)
二、为什么需要它?(对比普通 retry)
Kotlin Flow 还有一个简单的 retry(n):
flow.retry(3) // 出错就重试最多 3 次,无条件
但现实开发中,你往往需要更精细的控制,比如:
- 只对网络超时重试,不对认证失败重试
- 重试时加延迟(避免雪崩)
- 最多重试 2 次
- 用户点击“重试按钮”后再试(手动重试)
👉 这时候就要用 retryWhen!
三、基本语法
flow.retryWhen { cause: Throwable, attempt: Long ->// cause:抛出的异常// attempt:当前是第几次重试(从 0 开始)// 返回 true:重试// 返回 false:不再重试,异常向上抛出
}
⚠️ 注意:
retryWhen的 lambda 是 suspend 函数,你可以在里面delay()!
四、Android 实战例子 📱
场景:网络请求失败时智能重试
假设你有一个 Repository 方法:
suspend fun fetchUser(): User {// 模拟网络请求,可能抛出 IOException(网络问题)或 AuthException(登录过期)
}
在 ViewModel 中用 Flow 包装它:
val userFlow = flow {emit(fetchUser()) // 可能抛异常
}
现在加上 retryWhen:
val userWithRetry = userFlow.retryWhen { cause, attempt ->val maxRetries = 2// 1. 如果重试次数超过上限,不再重试if (attempt >= maxRetries) {return@retryWhen false}// 2. 只对特定异常重试(比如网络问题)if (cause is IOException || cause is SocketTimeoutException) {// 3. 重试前等一会儿(指数退避)delay((1000 * (attempt + 1)).toLong()) // 第1次等1秒,第2次等2秒return@retryWhen true // 重试!}// 其他异常(如认证失败)不重试false
}
✅ 这样:
- 网络抖动?自动重试 2 次,每次间隔更长
- Token 过期?直接报错,让用户重新登录
五、配合 UI:显示“重试按钮”
有时候你不想自动重试,而是等用户点击“重试”按钮再试。
这时候可以把重试逻辑和 UI 事件结合:
class UserViewModel : ViewModel() {private val _retrySignal = MutableSharedFlow<Unit>()val uiState = flow {emit(fetchUser())}.retryWhen { cause, _ ->// 等待用户点击“重试”_retrySignal.first() // 挂起,直到收到信号true // 收到信号就重试}.catch { error ->// 如果最终还是失败(比如用户没点重试),转为 Error 状态emit(UiState.Error(error))}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), UiState.Loading)fun onRetryClicked() {viewModelScope.launch {_retrySignal.emit(Unit) // 发送重试信号}}
}
UI 中:
when (uiState) {is UiState.Error -> Button(onClick = { viewModel.onRetryClicked() }) { Text("重试") }
}
✅ 这是 “手动重试” 的经典模式!
六、注意事项 & 常见坑
1. ❌ 不要无限重试!
retryWhen { _, _ -> true } // 永远重试 → 死循环!
✅ 一定要加次数限制或条件判断。
2. ⏱️ 重试前加 delay
否则可能瞬间发起大量请求,压垮服务器。
3. 🧪 只对“可恢复的错误”重试
- ✅ 可重试:网络超时、503 服务不可用
- ❌ 不可重试:401 未授权、400 参数错误、数据不存在
4. 🔁 retryWhen 会重新执行整个上游 Flow
比如:
flow {println("开始请求") // 每次重试都会打印!emit(api.call())
}.retryWhen { ... }
七、对比其他重试方式
| 方式 | 特点 | 适用场景 |
|---|---|---|
retry(n) | 简单,无条件重试 n 次 | 快速原型、简单任务 |
retryWhen | 灵活,可判断异常、加延迟、手动触发 | ✅ Android 生产环境推荐 |
| 手动循环 + try/catch | 完全控制 | 复杂逻辑,但代码啰嗦 |
八、总结一句话
retryWhen是 Flow 中实现“智能、可控重试”的核心操作符,特别适合 Android 开发中处理网络请求失败、数据库临时错误等场景,让你的 App 更健壮、用户体验更好。
配合 catch、StateFlow 和 UI 重试按钮,你可以构建出非常专业的错误处理流程!
现在使用 Retrofit + Coroutines + Flow 做网络层,retryWhen 几乎是必备技能!
