协程核心概念:CoroutineScope 与 CoroutineContext

图片描述

1. 什么是 CoroutineScope?

CoroutineScope(协程作用域)是协程的生命周期管家。它的源码非常简单,本质上只是对 CoroutineContext 的封装:

1
2
3
public interface CoroutineScope {
public val coroutineContext: CoroutineContext
}

它的核心作用: 追踪所有在该作用域内启动的协程。通过 scope.cancel(),你可以一键关闭该作用域下的所有子协程,从而彻底避免内存泄漏。


2. 在非 LifecycleOwner 类中手动管理协程

在普通 Java/Kotlin 类中,没有现成的 lifecycleScope 可用。此时需要手动构建并销毁作用域。

代码模板:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import kotlinx.coroutines.*

class MyRepository {
// 1. 定义 Job,推荐使用 SupervisorJob
private val repositoryJob = SupervisorJob()

// 2. 构建作用域:组合调度器与 Job
private val repositoryScope = CoroutineScope(Dispatchers.Main + repositoryJob)

fun fetchData() {
// 3. 使用作用域启动协程
repositoryScope.launch {
// ...
}
}

// 4. 在类销毁时手动取消
fun clear() {
repositoryJob.cancel()
}
}

3. 深入理解 CoroutineContext

如果说 CoroutineScope 是管家,那么 CoroutineContext 就是管家手里的配置清单。它由一组元素组成,每个元素都有唯一的 Key

核心元素列表:

  • Job: 控制协程生命周期。
  • CoroutineDispatcher: 决定在哪个线程运行。
  • CoroutineName: 协程名称(调试利器)。
  • CoroutineExceptionHandler: 未捕获异常的最后一道防线。

4. 源码解析:协程的 “+” 号代表什么?

当我们写下 Dispatchers.Main + Job() 时,其实是调用了 CoroutineContextplus 操作符。

核心源码分析:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public operator fun plus(context: CoroutineContext): CoroutineContext =
if (context === EmptyCoroutineContext) this else
context.fold(this) { acc, element ->
val removed = acc.minusKey(element.key)
if (removed === EmptyCoroutineContext) element else {
// 确保拦截器(Interceptor)永远在末尾
val interceptor = removed[ContinuationInterceptor]
if (interceptor == null) CombinedContext(removed, element) else {
val left = removed.minusKey(ContinuationInterceptor)
if (left === EmptyCoroutineContext) CombinedContext(element, interceptor) else
CombinedContext(CombinedContext(left, element), interceptor)
}
}
}

逻辑拆解:

  1. 右侧优先:相同 Key 的元素,右边的会覆盖左边的。
  2. 拦截器置后:确保调度器永远处于链表的末尾,以实现快速查找性能。
,