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

Retrofit忽略协程取消,超时请求仍等待响应问题求助

问题分析与解决思路

核心原因

Retrofit本身不会主动忽略协程取消,但两种场景会导致你遇到的问题:一是如果用了**阻塞式的Call.execute()**而非挂起函数,协程的取消信号无法传递到网络请求线程;二是OkHttp的连接池/请求队列在服务器高负载时出现请求堆积,当之前被取消的请求占用的连接被复用时,可能触发历史请求的回调逻辑。

具体解决步骤

  • 强制使用Retrofit挂起函数API:接口定义必须用suspend fun,让协程的取消信号能通过CoroutineContext传递到OkHttp执行流程,协程取消时OkHttp会主动中断当前请求。
    // 正确的接口定义示例
    interface ApiService {
        @GET("target/api")
        suspend fun fetchTabData(): Response<TabDataModel>
    }
    
  • 给OkHttp添加协程取消拦截器:自定义拦截器,在请求执行前检查当前协程是否已取消,若已取消直接抛出CancellationException,避免请求继续执行;同时缩短超时参数,减少高负载下的请求堆积。
    class CoroutineCancelInterceptor : Interceptor {
        override fun intercept(chain: Interceptor.Chain): Response {
            if (coroutineContext.isActive.not()) {
                throw CancellationException("Coroutine has been cancelled")
            }
            return chain.proceed(chain.request())
        }
    }
    
    // OkHttp客户端配置
    val okHttpClient = OkHttpClient.Builder()
        .addInterceptor(CoroutineCancelInterceptor())
        .connectTimeout(8, TimeUnit.SECONDS)
        .readTimeout(8, TimeUnit.SECONDS)
        .writeTimeout(8, TimeUnit.SECONDS)
        .build()
    
  • ViewModel中独立管理请求Job:每个标签页的请求绑定一个独立Job,切换标签页时先取消之前的Job,避免残留请求的回调触发。
    class TabViewModel : ViewModel() {
        private var currentFetchJob: Job? = null
    
        fun loadTabData() {
            currentFetchJob?.cancel() // 取消未完成的旧请求
            currentFetchJob = viewModelScope.launch {
                try {
                    val response = apiService.fetchTabData()
                    // 处理正常响应
                } catch (e: Exception) {
                    if (e !is CancellationException) {
                        // 处理非取消类异常
                    }
                }
            }
        }
    
        override fun onCleared() {
            currentFetchJob?.cancel()
            super.onCleared()
        }
    }
    
  • 关闭OkHttp默认重试机制:服务器高负载时,超时可能触发OkHttp的自动重试,导致请求重复执行,直接关闭该机制。
    val okHttpClient = OkHttpClient.Builder()
        .retryOnConnectionFailure(false)
        .build()
    

额外排查点

  • 检查是否使用了全局单例ApiService却未隔离请求上下文,导致不同标签页的请求共享了同一个Call实例,引发回调混乱。
  • 确认ViewModel的viewModelScope是否正确绑定生命周期,避免协程在Fragment销毁后仍持有引用。

内容的提问来源于stack exchange,提问作者sahar shamloo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 12:30:42