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。

相关推荐
黄林晴8 小时前
Google Play 发版链路全面重构:合规前置、审核自动化、生态全面收紧
android·google
通玄10 小时前
Jetpack Compose 入门系列(四):动画基本使用
android
杉氧10 小时前
Kotlin 协程深度解析②:生存指南——掌握结构化并发的生命线
android·kotlin
故渊at10 小时前
第四板块:Android 输入系统与触控事件 | 第十五篇:InputReader 与 InputDispatcher 的触控流水线
android·anr·输入系统·inputdispatcher·inputreader·触控事件·inputevent
方白羽10 小时前
Vibe Coding 四个核心阶段
android·前端·app
潘潘潘12 小时前
Android网络结构分析——有线网络
android
踏雪羽翼12 小时前
Android OpenGL实现十几种美颜功能
android
Android小码家13 小时前
BootAnimation+SE+开机MP4动画播放
android·framework
加农炮手Jinx14 小时前
Flutter for OpenHarmony:pub_updater 命令行工具自动更新专家(DevOps 运维必备) 深度解析与鸿蒙适配指南
android·运维·网络·flutter·华为·harmonyos·devops