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

Kotlin+Retrofit:仅在Fragment中调用出现HTTP请求Canceled异常

解决Fragment中接口调用抛出HTTP Canceled与JobCancellationException的问题

问题背景

在Fragment中发起接口调用时,出现HTTP FAILED: java.io.IOException: Canceled错误,ViewModel捕获到以下异常:

kotlinx.coroutines.JobCancellationException: Job was cancelled; job=SupervisorJobImpl{Cancelling}@c05b47

但相同的接口调用逻辑在MainActivity中可正常执行,仅在Fragment中触发异常。

核心原因

该问题本质是协程被生命周期触发取消,进而导致OkHttp中断请求:

  • ViewModel的viewModelScope会绑定其宿主的生命周期。若Fragment使用by viewModels()获取ViewModel,ViewModel的生命周期与Fragment绑定,当Fragment被替换、移除或因屏幕旋转重建时,viewModelScope会自动取消所有运行中的协程。
  • OkHttp的suspend请求会响应协程取消信号,直接中断网络请求并抛出IOException: Canceled。
  • Activity的生命周期更长,除非Activity本身被销毁,否则其绑定的ViewModel协程不会被轻易取消,因此请求可正常完成。

解决方案

1. 切换为Activity级别的ViewModel

若需要Fragment销毁后请求仍继续执行,或多个Fragment共享同一ViewModel状态,应将ViewModel与Activity绑定:

// 在Fragment中获取Activity级别的ViewModel
val syncViewModel: SyncViewModel by activityViewModels()

此时ViewModel的生命周期与Activity一致,Fragment的销毁不会触发协程取消。

2. 过滤协程取消异常

在ViewModel的异常处理中,区分正常的生命周期取消与业务异常,避免不必要的错误提示:

fun initialSync(syncRequest: SyncRequest) {
    _uiState.value = SyncUiState.Loading
    viewModelScope.launch(coroutineDispatcherProvider.IO()) {
        try {
            val result = getSyncUseCase.execute(syncRequest)
            _uiState.value = SyncUiState.Loaded(SyncItemUiState(result))
        } catch (error: JobCancellationException) {
            // 协程因生命周期取消,无需处理
        } catch (error: Exception) {
            _uiState.value = SyncUiState.Error(error.message.toString())
        }
    }
}

3. 确保请求在Fragment活跃状态下发起

在触发请求前检查Fragment的活跃状态,避免在Fragment即将销毁时发起请求:

button.setOnClickListener {
    // 检查Fragment是否已添加到Activity
    if (!isAdded) return@setOnClickListener
    val syncRequest = SyncRequest(0, 1,false,false)
    syncViewModel.initialSync(syncRequest)
}

内容的提问来源于stack exchange,提问作者A. Cedano

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 03:47:06