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
相关产品推荐
相关产品推荐

