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。

相关推荐
PyHaVolask20 小时前
CSRF跨站请求伪造
android·前端·csrf
走在路上的菜鸟21 小时前
Android学Flutter学习笔记 第五节 Android视角认知Flutter(插件plugins)
android·学习·flutter
2501_9159214321 小时前
如何在苹果手机上面进行抓包?iOS代理抓包,数据流抓包
android·ios·智能手机·小程序·uni-app·iphone·webview
_李小白1 天前
【Android 美颜相机】第五天:GPUImageFilterTools
android·数码相机
冬奇Lab1 天前
【Kotlin系列05】集合框架:从Java的冗长到函数式编程的优雅
android·kotlin·编程语言
冬奇Lab1 天前
稳定性性能系列之十四——电量与网络优化:Battery Historian与弱网处理实战
android·性能优化·debug
Coffeeee1 天前
了解一下Android16更新事项,拿捏下一波适配
android·前端·google
用户41659673693551 天前
深入解析安卓 ELF 16KB 页对齐:原生编译与脚本修复的权衡
android
恋猫de小郭1 天前
Compose Multiplatform 1.10 Interop views 新特性:Overlay 和 Autosizing
android·flutter·macos·kotlin·github·objective-c·cocoa
胖虎11 天前
Android 文件下载实践:基于 OkHttp 的完整实现与思考
android·okhttp·下载文件·安卓下载·安卓中的下载