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

Kotlin withTimeout无法取消后台线程的OkHttp请求问题排查

问题分析与解决方案

你的超时实现无效的核心原因有两个:

  1. 错误使用runBlocking+async包装OkHttp请求,导致协程超时无法中断底层的阻塞IO调用
  2. 协程超时仅会取消协程任务,但不会主动终止OkHttp的网络请求,请求会在后台继续执行

问题拆解

  • runBlocking { async { call.execute() } }完全多余,它会在独立线程池里执行阻塞的call.execute(),协程的withTimeout无法中断这个线程的阻塞操作,超时后请求仍会继续运行
  • 即使协程抛出TimeoutCancellationException,OkHttp的同步请求不会响应协程的取消信号,导致你看不到"Here 2"的日志(甚至超时后请求还会正常返回)

修正后的实现

移除多余的协程包装,同时在协程取消时主动终止OkHttp请求:

suspend fun fetchData(): Result {
    var url = ""
    return try {
        // ... 构建request的逻辑
        val call = NetworkProvider.okHttpClient.newCall(request)
        url = call.request().toString()

        val response = withTimeout(1500L) {
            // 监听协程取消事件,主动终止OkHttp请求
            coroutineContext.job.invokeOnCompletion { cause ->
                if (cause is CancellationException) {
                    call.cancel()
                }
            }
            // 直接执行同步请求(已在Dispatchers.IO协程中,无需额外包装)
            call.execute()
        }

        // 处理响应结果
        if (response.isSuccessful) {
            // 解析响应数据并返回Success
            Result.Success(...)
        } else {
            Result.Failure(IOException("HTTP错误码: ${response.code}"))
        }
    } catch (ex: TimeoutCancellationException) {
        Log.d("---", "Here 2: 请求超时")
        Result.Failure(ex)
    } catch (ex: IOException) {
        Log.d("---", "网络请求失败: ${ex.message}")
        Result.Failure(ex)
    }
}

关键说明

  • OkHttp的call.execute()是阻塞IO操作,必须在Dispatchers.IO协程中执行(你的调用已经满足这个条件)
  • 通过coroutineContext.job.invokeOnCompletion监听协程取消,一旦超时触发协程取消,立即调用call.cancel()终止OkHttp请求,避免无效的后台请求
  • 移除runBlocking和async,直接在withTimeout块内执行请求,保证协程超时能正确触发异常捕获

内容的提问来源于stack exchange,提问作者Ayusch Jain

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 16:42:23