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。

相关推荐
DogDaoDao4 小时前
Android 硬件编码器参数完全指南:MediaCodec 深度解析
android·音视频·视频编解码·h264·硬编码·视频直播·mediacodec
JohnnyDeng945 小时前
Android 自定义 View:Canvas 绘图与事件分发深度解析
android
Android小码家8 小时前
Framework之Launcher小窗开发
android·framework·虚拟屏·小窗
赏金术士8 小时前
第七章:状态管理实战与架构总结
android·ui·kotlin·compose
颂love9 小时前
MySQL的执行流程
android·数据库·mysql
云起SAAS14 小时前
抖音小游戏源码 - 消消乐 | 含激励广告+成就系统 | 开箱即用商业级消除游戏模板
android·游戏·广告联盟·看激励广告联盟流量主·抖音小游戏源码 - 消消乐
大貔貅喝啤酒15 小时前
基于Windows下载安装Android Studio 3.3.2版本教程(2026详细图文版)
android·java·windows·android studio
程序员码歌15 小时前
OpenSpec 到 Superpowers:AI 编码从说清到做对
android·前端·人工智能
2501_9151063215 小时前
深入解析无源码iOS加固原理与方案,保护应用安全
android·安全·ios·小程序·uni-app·cocoa·iphone
黄林晴19 小时前
重磅官宣:Android UI 开发正式进入 Compose-first 时代
android·google io