Kotlin协程:Job.cancel() 和 Scope.cancel() 的区别详解!!!

在 Kotlin 协程中,Job.cancel()Scope.cancel() 都用于取消协程,但它们的作用范围和行为有重要区别:

1. Job.cancel()

kotlin 复制代码
val job = launch {
    // 协程体
}
job.cancel()  // 只取消这个特定的 job

特点:

  • 只取消单个 Job 及其子协程
  • 不会影响同一作用域中的其他 Job
  • Job 被取消后,仍然可以附加新的子协程
kotlin 复制代码
val job1 = scope.launch { /* 任务1 */ }
val job2 = scope.launch { /* 任务2 */ }

job1.cancel()  // 只取消 job1,job2 继续运行

2. Scope.cancel()

kotlin 复制代码
scope.cancel()  // 取消整个作用域及其所有协程

特点:

  • 取消整个作用域中的所有协程
  • 作用域被取消后,不能再启动新的协程
  • 所有子 Job 都会被取消
kotlin 复制代码
val scope = CoroutineScope(Dispatchers.IO)

scope.launch { /* 任务1 */ }
scope.launch { /* 任务2 */ }

scope.cancel()  // 取消所有任务,且 scope 不能再 launch 新协程

3. 关键区别对比

特性 Job.cancel() Scope.cancel()
作用范围 单个 Job + 其子协程 整个作用域 + 所有协程
后续使用 可继续启动新协程 作用域已失效,不能启动新协程
状态影响 Job 进入 Cancelling 状态 Scope 和所有 Job 都取消
典型场景 取消特定任务 清理整个界面/组件资源

4. 实际例子

Job.cancel() 示例

kotlin 复制代码
val scope = CoroutineScope(SupervisorJob())

val jobA = scope.launch {
    delay(1000)
    println("Job A 完成")
}

val jobB = scope.launch {
    delay(2000)
    println("Job B 完成")
}

// 只取消 jobA,jobB 继续运行
jobA.cancel()

// scope 仍然可用,可以启动新协程
scope.launch { println("新协程") }

Scope.cancel() 示例

kotlin 复制代码
val scope = CoroutineScope(SupervisorJob())

scope.launch { /* 任务1 */ }
scope.launch { /* 任务2 */ }

// 取消整个作用域
scope.cancel()

// 这里会抛出异常:Scope is cancelled
scope.launch { println("这不会执行") }

5. 重要细节

Scope.cancel() 的内部实现

kotlin

kotlin 复制代码
// CoroutineScope 的 cancel() 实际上调用了关联 Job 的 cancel()
public fun CoroutineScope.cancel(cause: CancellationException? = null) {
    val job = coroutineContext[Job]
        ?: error("Scope cannot be cancelled because it does not have a job")
    job.cancel(cause)
}

结构化并发的体现

  • 在结构化并发中,通常使用 scope.cancel() 来清理资源
  • ViewModel 销毁时:viewModelScope.cancel()
  • Activity 销毁时:lifecycleScope.cancel()

异常处理差异

kotlin 复制代码
// Job.cancel() 可以指定原因
job.cancel("用户取消操作")

// Scope.cancel() 同样可以
scope.cancel(TimeoutCancellationException("超时"))

6. 最佳实践建议

  1. 使用 Job.cancel() 当需要:

    • 取消特定的后台任务
    • 实现可取消的单个操作
    • 不影响其他并行任务
  2. 使用 Scope.cancel() 当需要:

    • 组件(Activity/Fragment)销毁时清理资源
    • 取消整个工作流的所有任务
    • 确保没有协程泄露
  3. 在 ViewModel 中

kotlin 复制代码
class MyViewModel : ViewModel() {
    private val customScope = CoroutineScope(SupervisorJob())
    
    fun startWork() {
        customScope.launch { /* 工作 */ }
    }
    
    override fun onCleared() {
        customScope.cancel()  // 清理自定义作用域
        super.onCleared()
    }
    
    // viewModelScope 会自动管理,无需手动取消
    fun useViewModelScope() {
        viewModelScope.launch { /* 自动绑定生命周期 */ }
    }
}

核心总结Job.cancel()选择性取消Scope.cancel()全面清理。在结构化并发中,通常让作用域管理生命周期,仅在特殊情况下单独取消特定 Job。

相关推荐
Zender Han2 小时前
Flutter 图片裁剪插件 image_cropper 最新版介绍与使用教程
android·flutter·ios
方白羽2 小时前
Android 与 iOS 动态更换应用图标实现方案
android·ios·app
nono牛2 小时前
MTK平台Android init.rc服务详解实例
android·gitee
Ricardo_03243 小时前
关于死锁问题的学习总结
android·java
熬夜喝酒写代码3 小时前
Android Framework之编译源码
android
·云扬·3 小时前
MySQL排序与分组性能优化:从原理到实践
android·mysql·性能优化
EQ-雪梨蛋花汤3 小时前
【NDK / JNI】Sceneform-EQR 集成 Filament JNI 源码:关键点与逐步操作记录
android·jni·sceneform-eqr
消失的旧时光-19433 小时前
从命令式跳转到声明式路由:前端、Android、Flutter 的一次统一演进
android·前端·flutter·状态模式
不急不躁1233 小时前
Android16 跳过GMS测试项
android·java