1. 核心机制
-
suspendCancellableCoroutine
提供可取消的挂起协程能力,通过continuation.invokeOnCancellation
注册取消时的清理逻辑。当外部触发取消(如超时、父协程取消)时,自动执行清理并抛出CancellationException
。 -
withTimeoutOrNull
在指定超时时间(如 2000ms)后,若协程未完成,会发送取消信号。与suspendCancellableCoroutine
配合时,能立即终止挂起状态。
2. 最佳实践模板
suspend fun executeWithTimeout(): ResultType? {
return withTimeoutOrNull(TIMEOUT_MS) {
suspendCancellableCoroutine { cont ->
val listener = createListener { result ->
if (cont.isActive) cont.resume(result)
}
cont.invokeOnCancellation {
removeListener(listener) // 清理资源
}
registerListener(listener) // 启动异步操作
}
}
}
3. 工作流程(含超时场景)
withTimeoutOrNull(3000) { // 3秒超时控制
suspendCancellableCoroutine { continuation ->
// 注册取消时的资源清理
continuation.invokeOnCancellation {
unregisterListener(listener) // 关键:防止泄漏
}
// 异步操作(例如关闭WiFi)
registerListener(listener)
closeWifi()
// 成功回调(正常情况)
fun onSuccess() {
continuation.resume(true) // 手动恢复协程
}
}
} ?: run {
Log.d(TAG, "超时啦")
false // 超时返回null,通过Elvis运算符转为false
}
4. 超时触发的详细时序
时间轴 | 事件 | 结果 |
---|---|---|
T=0ms | 进入 withTimeoutOrNull 块 | 开始计时 |
T=0ms | 调用 suspendCancellableCoroutine | 挂起协程 |
T<2000ms | 收到 onStateChanged(WIFI_STATE_DISABLED) | 调用 resume(true) ,协程完成 |
T≥2000ms | 未收到回调,超时触发 | 执行 invokeOnCancellation ,返回 null |
任何时刻 | 父协程取消 | 同超时逻辑 |
5.为什么用suspendCancellableCoroutine,suspendCoroutine不行?
根本原因在于:
-
suspendCancellableCoroutine
支持外部取消(如withTimeout
超时、父协程取消),而suspendCoroutine
不支持。
suspendCancellableCoroutine
-
自动响应取消:当外部触发超时(如
withTimeout
)或协程被取消时,suspendCancellableCoroutine
会立即执行invokeOnCancellation
回调,并抛出CancellationException
,从而结束挂起状态
suspendCoroutine
-
不支持取消:即使外部调用
withTimeout
,suspendCoroutine
仍会继续挂起,直到手动调用continuation.resume()
。 -
超时失效:
withTimeout
试图取消协程,但suspendCoroutine
不会响应取消请求,导致协程永远挂起,超时机制失效。
特性 | suspendCancellableCoroutine | suspendCoroutine |
---|---|---|
是否支持取消 | ✅ 是(自动响应 withTimeout ) | ❌ 否 |
超时是否生效 | ✅ 是 | ❌ 否 |
资源清理 | ✅ 通过 invokeOnCancellation | ❌ 需手动处理 |
推荐使用场景 | 所有需要超时/取消的挂起操作 | 无需取消的简单场景 |