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。

相关推荐
祖国的好青年28 分钟前
VS Code 搭建 React Native 开发环境(Windows 实战指南)
android·windows·react native·react.js
黄林晴1 小时前
警惕!AGP 9.2 别只改版本号,R8 规则与构建链路全线收紧
android·gradle
小米渣的逆袭1 小时前
Android ADB 完全使用指南
android·adb
儿歌八万首1 小时前
Jetpack Compose Canvas 进阶:结合 animateFloatAsState 让自定义图形动起来
android·动画·compose
zhangphil2 小时前
Android Page 3 Flow读sql数据库媒体文件,Kotlin
android·kotlin
神探小白牙3 小时前
echarts,3d堆叠图
android·3d·echarts
李白的天不白3 小时前
如何项目发布到github上
android·vue.js
summerkissyou19873 小时前
Android-RTC、NTP 和 System Time(系统时间)
android
小书房3 小时前
Kotlin使用体验及理解1
android·开发语言·kotlin
撩得Android一次心动4 小时前
Android Navigation 组件全面讲解
android·jetpack·navigation