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

如何在Kotlin协程中取消Retrofit发起的文件上传请求?

用Kotlin协程实现可取消的Retrofit图片上传

当然可以用Kotlin协程实现这个需求,而且完全不需要依赖RxJava——你可能只是没注意到Retrofit对协程的原生支持已经帮我们处理了请求取消的核心逻辑😉

核心原理:Retrofit + 协程的自动请求取消

Retrofit从2.6.0版本开始原生支持suspend函数,更重要的是:当协程被取消时,Retrofit会自动调用底层OkHttp请求的cancel()方法。这意味着你不需要手动处理协作式取消的细节——框架已经把协程取消和网络请求取消绑定在一起了,和RxJava里调用Disposable.dispose()的效果完全一致。

具体实现步骤

1. 定义支持协程的Retrofit接口

首先把你的上传接口改成suspend函数形式:

interface ApiService {
    @Multipart
    suspend fun uploadImage(
        @Part file: MultipartBody.Part
    ): Response<UploadResult> // UploadResult是你的后端返回模型
}

2. 管理上传协程的生命周期

你可以用Job对象来持有上传的协程,就像RxJava里的Disposable一样,在需要取消的时候调用cancel()即可。结合Android的viewModelScope(或者其他自定义CoroutineScope)还能自动处理生命周期,避免内存泄漏:

class UploadViewModel(private val apiService: ApiService) : ViewModel() {
    private var uploadJob: Job? = null

    // 开始上传
    fun startUpload(file: File) {
        // 如果已有上传任务,先取消
        uploadJob?.cancel()
        
        uploadJob = viewModelScope.launch {
            val requestBody = file.asRequestBody("image/*".toMediaType())
            val filePart = MultipartBody.Part.createFormData("file", file.name, requestBody)
            
            try {
                val response = apiService.uploadImage(filePart)
                if (response.isSuccessful) {
                    // 处理上传成功逻辑
                } else {
                    // 处理后端返回的错误
                }
            } catch (e: CancellationException) {
                // 协程被取消(用户点击了取消按钮),这里可以提示用户上传终止
            } catch (e: IOException) {
                // 处理网络异常(比如断网)
            } finally {
                uploadJob = null
            }
        }
    }

    // 取消上传
    fun cancelUpload() {
        uploadJob?.cancel()
        uploadJob = null
    }

    // ViewModel销毁时自动取消未完成的上传
    override fun onCleared() {
        super.onCleared()
        uploadJob?.cancel()
    }
}

关于协程协作式取消的误区

你提到的“需要在循环或分块间隙调用挂起函数才能取消”,是针对自定义的耗时挂起任务(比如自己手动分块上传、循环处理数据等)。但在Retrofit的场景下,网络请求的执行是由OkHttp底层处理的,Retrofit已经在suspend函数内部实现了协程取消的协作逻辑——当协程被取消时,它会立即调用OkHttp请求的cancel()方法,终止网络连接,和RxJava的取消行为完全一致。

如果是你自己实现分块上传(比如把大文件分成多个小块逐个上传),那确实需要在每块上传前调用ensureActive()或者yield()来检查协程状态,确保能及时响应取消,但用Retrofit原生的multipart上传完全不需要这一步。

协程 vs RxJava:哪个更适合?

在这个场景下,协程的实现反而更简洁:

  • 不需要引入RxJava的依赖,代码更轻量化
  • 用Job管理协程比Disposable更符合Kotlin的语法风格
  • 结合viewModelScope/lifecycleScope可以自动和Android生命周期绑定,减少内存泄漏风险

RxJava当然也能完成任务,但协程的方案更贴合Kotlin生态,代码可读性更高,没有额外的线程调度学习成本(协程的调度器更直观)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 07:10:21