一、基础异常捕获机制
-
**
try/catch代码块**
适用于同步代码和 Flow 收集阶段的异常捕获。flow { emit(1); throw RuntimeException() } .collect { value -> try { process(value) } catch (e: Exception) { println("收集异常: $e") } }- 作用:捕获
collect块内的处理逻辑异常1。 - 局限:无法捕获
flow构建器或中间操作符中的异常16。
- 作用:捕获
二、Flow 流专用异常操作符
1. catch:统一处理上游异常
-
捕获范围:
flow构建器和map、filter等中间操作符抛出的异常26。 -
示例:
flow { emit(1) throw IOException("网络错误") } .catch { e -> emit(-1) // 异常后发射兜底数据 println("发射异常: $e") } .collect { println(it) }输出:
textCopy Code 1 → -1
2. onCompletion:流结束回调
-
功能:无论流正常结束还是异常终止,均会触发。
flow { emit(1) } .onCompletion { cause -> if (cause != null) println("流异常终止: $cause") else println("流正常结束") } .collect { ... }- 注意:
cause参数为null表示正常结束26。
- 注意:
3. retryWhen:条件式重试
-
场景:网络请求失败后按策略重试。
flow { fetchData() } .retryWhen { cause, attempt -> (cause is IOException) && (attempt < 3) } .collect { ... }- 逻辑:当异常为
IOException且重试次数小于 3 时自动重试2。
- 逻辑:当异常为
三、操作符对比与选型
| 操作符 | 捕获阶段 | 典型场景 | 引用 |
|---|---|---|---|
try/catch | 收集阶段 (collect) | 消费端业务逻辑异常处理 | 16 |
catch | 生产阶段 (emit) | 统一处理数据生成异常 | 12 |
retryWhen | 全流程 | 网络请求失败后有限次重试 | 2 |
onCompletion | 全流程 | 资源清理或状态记录 | 26 |
四、最佳实践
-
分层处理异常
- 生产端:使用
catch恢复默认值或兜底数据12。 - 消费端:结合
try/catch处理业务逻辑异常1。
- 生产端:使用
-
资源释放
在onCompletion或finally块中释放资源(如关闭文件句柄):flow { readFile() } .onCompletion { closeFile() } .collect { ... } -
调试与监控
通过onCompletion记录流执行状态和异常信息26。
五、完整示例
案例一、
fun fetchDataFlow(): Flow<Data> = flow {
val data = api.fetch() // 可能抛出 IOException
emit(data)
}.catch { e ->
emit(Data.EMPTY) // 异常时返回空数据
logError(e) // 记录错误日志
}.retryWhen { cause, attempt ->
(cause is IOException) && (attempt < 2) // 最多重试 2 次
}.onCompletion { cause ->
if (cause == null) println("数据流正常结束")
}
案例二
val getNumbers = flowOf(1,2,3,4,5).onEach { delay(2000) }
fun main() = runBlocking {
try {
getNumbers.collect{
println("$it")
if (it==3) throw KotlinNullPointerException("下游计算过程中出现问题")
}
}catch (e:Exception){
println("e:$e")
}
//对上游出现异常采用声明式
flow{
listOf(1,2,3,4,5,6,7,8).forEach{values -> emit(values)
delay(2000)
if(values == 5) throw KotlinNullPointerException("上游计算过程发出异常信息")
}
}.catch {
println("异常信息:e $it")
emit(-1)
}.onEach {
delay(2000)
}.collect{
println(it)
}
//对上游出现异常采用声明式
flow{
listOf(100).forEach{values -> emit(values)
delay(2000)
throw KotlinNullPointerException("上游计算过程发出异常信息")
}
}.catch {
emit(200)
}.onEach {
delay(2000)
}.collect{
println(it)
}
}
六
通过合理组合 catch、retryWhen 等操作符,可实现健壮的异步数据处理流程,兼顾异常恢复与资源管理