一、协程上下文与调度器
协程上下文的构成与组合
协程上下文(CoroutineContext) 是一组元素的集合,包括 Job、调度器、异常处理器等。
- 
主要元素类型:
- Job:控制协程生命周期
 - CoroutineDispatcher:指定协程运行线程
 - CoroutineName:协程名称,用于调试
 - CoroutineExceptionHandler:处理未捕获异常
 
 - 
上下文组合 :使用
+运算符组合多个上下文元素 
            
            
              kotlin
              
              
            
          
          val context = Dispatchers.Main + CoroutineName("NetworkRequest") + Job()
        - 上下文继承:协程从启动它的协程继承上下文,可通过参数覆盖
 
自定义协程上下文元素
实现自定义上下文元素需继承 AbstractCoroutineContextElement 或实现 CoroutineContext.Element 接口。
            
            
              kotlin
              
              
            
          
          class TraceIdContext(val traceId: String) : AbstractCoroutineContextElement(Key) {
    companion object Key : CoroutineContext.Key<TraceIdContext>
}
// 使用自定义上下文
launch(Dispatchers.Main + TraceIdContext("trace-123")) {
    val traceId = coroutineContext[TraceIdContext]?.traceId
}
        调度器的高级应用与切换策略
CoroutineDispatcher 决定协程在哪个线程或线程池执行。
- 
常用调度器:
- Dispatchers.Main:主线程,用于UI操作
 - Dispatchers.IO:IO密集型操作
 - Dispatchers.Default:CPU密集型计算
 - Dispatchers.Unconfined:非受限调度器
 
 - 
调度器切换 :使用
withContext临时切换调度器 
            
            
              kotlin
              
              
            
          
          suspend fun fetchData(): Data = withContext(Dispatchers.IO) {
    // IO操作
    apiService.getData()
}
        - 高级切换策略 :
- 避免不必要的线程切换
 - 长时间运行的任务使用 
Dispatchers.Default - 实现自定义调度器满足特定需求
 
 
二、协程作用域与结构化并发
自定义CoroutineScope实现
CoroutineScope 管理协程生命周期,确保协程不会泄漏。
- 自定义作用域实现:
 
            
            
              kotlin
              
              
            
          
          class MyViewModel : ViewModel(), CoroutineScope {
    private val job = SupervisorJob()
    override val coroutineContext: CoroutineContext
        get() = Dispatchers.Main + job + CoroutineName("MyViewModel")
    // 清除时取消所有协程
    override fun onCleared() {
        super.onCleared()
        job.cancel()
    }
    // 使用作用域启动协程
    fun loadData() {
        launch {
            // 执行任务
        }
    }
}
        SupervisorJob与异常隔离
SupervisorJob 允许子协程失败而不影响其他子协程,实现异常隔离。
- 与普通Job的区别 :
- 普通Job:一个子协程失败,所有子协程和父协程都会被取消
 - SupervisorJob:一个子协程失败,不影响其他子协程和父协程
 
 
            
            
              kotlin
              
              
            
          
          val supervisorScope = CoroutineScope(Dispatchers.Main + SupervisorJob())
// 子协程失败不会取消其他子协程
supervisorScope.launch {
    // 子协程1
}
supervisorScope.launch {
    // 子协程2,失败不会影响子协程1
}
        - supervisorScope函数:创建一个使用SupervisorJob的作用域
 
协程生命周期管理与取消传播
- 协程取消是协作式的:协程必须检查取消状态
 - 取消传播:父协程取消会传播给所有子协程
 - 生命周期管理实践:
 
            
            
              kotlin
              
              
            
          
          val scope = CoroutineScope(Dispatchers.Main + Job())
// 启动多个子协程
val job1 = scope.launch { ... }
val job2 = scope.launch { ... }
// 取消所有子协程
scope.cancel()
// 或取消单个协程
job1.cancel()
        - 检测取消 :使用 
isActive属性或ensureActive()函数 
            
            
              kotlin
              
              
            
          
          while (isActive) {
    // 执行循环任务
}
        三、高级挂起函数与流
自定义挂起函数的实现模式
挂起函数 是可以暂停执行并稍后恢复的函数,使用 suspend 关键字修饰。
- 回调转换模式 :使用 
suspendCancellableCoroutine将回调API转换为挂起函数 
            
            
              kotlin
              
              
            
          
          suspend fun fetchData(): Result = suspendCancellableCoroutine { continuation ->
    val call = apiService.makeRequest()
    call.enqueue(object : Callback<Result> {
        override fun onSuccess(result: Result) {
            continuation.resume(result)
        }
        override fun onFailure(e: Exception) {
            continuation.resumeWithException(e)
        }
    })
    // 处理取消
    continuation.invokeOnCancellation {
        call.cancel()
    }
}
        - 异步结果模式 :使用 
coroutineScope并发执行多个挂起函数 
            
            
              kotlin
              
              
            
          
          suspend fun loadAllData(): CombinedResult = coroutineScope {
    val data1 = async { fetchData1() }
    val data2 = async { fetchData2() }
    CombinedResult(data1.await(), data2.await())
}
        Flow的高级操作符与背压处理
在实际开发中,Flow的高级操作符能够显著简化复杂数据流处理逻辑。例如,transform操作符相比map提供了更大的灵活性,可以在转换过程中发射多个值或跳过某些值:
            
            
              kotlin
              
              
            
          
          flowOf(1, 2, 3)
    .transform { value ->
        if (value % 2 == 0) {
            emit("Even: $value")
        } else {
            emit("Odd: $value")
            emit("Odd squared: ${value * value}")
        }
    }
    .collect { println(it) }
// 输出: Odd: 1, Odd squared: 1, Even: 2, Odd: 3, Odd squared: 9
        背压处理是Flow的核心优势之一,它解决了生产者和消费者处理速度不匹配的问题。Flow通过以下策略实现背压管理:
- 
缓冲策略 :使用
buffer()操作符为流设置缓冲区,允许生产者在消费者处理期间继续发射数据kotlinflow { repeat(5) { delay(100) // 生产者较慢 emit(it) } } .buffer(3) // 设置缓冲区大小 .collect { delay(300) // 消费者较慢 println(it) } - 
并发处理 :
conflate()操作符会丢弃中间值,只保留最新值,适用于UI更新等场景kotlin// 当新值产生时,如果前一个值还未被处理,则直接丢弃前一个值 flow { repeat(10) { delay(100) emit(it) } } .conflate() .collect { delay(300) println(it) } - 
背压感知 :Flow的
collectLatest操作符会在新值到达时取消当前正在处理的旧值,确保始终处理最新数据kotlinflow { emit(1) delay(50) emit(2) delay(50) emit(3) } .collectLatest { value -> println("Processing $value") delay(100) // 模拟耗时处理 println("Finished processing $value") } // 输出: Processing 1, Processing 2, Processing 3, Finished processing 3 
Flow还提供了onBackpressureBuffer(), onBackpressureDrop()和onBackpressureLatest()等操作符,允许开发者根据具体场景自定义背压处理策略,确保数据流处理的效率和稳定性。
Flow 是冷流,用于异步发射多个值,支持背压处理。
- 常用高级操作符 :
transform:转换发射的值combine/zip:组合多个流debounce:防抖,忽略短时间内的频繁发射distinctUntilChanged:仅发射与前一个不同的值retry/retryWhen:失败时重试
 
            
            
              kotlin
              
              
            
          
          val searchResults = searchQueryFlow
    .debounce(300)
    .distinctUntilChanged()
    .transform { query ->
        emit(Loading)
        try {
            val results = repository.search(query)
            emit(Success(results))
        } catch (e: Exception) {
            emit(Error(e))
        }
    }
    .flowOn(Dispatchers.IO)
        - 背压处理策略 :
- 缓冲:
buffer() - 合并:
conflate() - 最新值:
collectLatest() 
 - 缓冲:
 
Channel的高级应用与并发模式
Channel 用于协程间通信,支持发送和接收数据。
- 
Channel类型:
- Rendezvous:无缓冲区,发送和接收需同时准备好
 - Buffered:有固定大小缓冲区
 - Unlimited:无界缓冲区
 - Conflated:只保留最新元素
 
 - 
生产者-消费者模式:
 
            
            
              kotlin
              
              
            
          
          val channel = Channel<Int>(capacity = 10)
// 生产者
launch {
    for (i in 1..100) {
        channel.send(i)
    }
    channel.close()
}
// 消费者
launch {
    for (value in channel) {
        process(value)
    }
}
        - 使用produce和actor构建器:
 
            
            
              kotlin
              
              
            
          
          // 生产者协程
val producer = produce<Int> {
    repeat(10) { send(it) }
}
// 消费者协程
val actor = actor<Int> {
    for (msg in channel) {
        println("Received: $msg")
    }
}
        四、协程异常处理与测试
异常传播与捕获机制
协程异常传播遵循特定规则,取决于协程的启动方式和作用域。
- 
异常传播路径:
- 直接使用 
launch启动的协程:异常向上传播给父协程 - 使用 
async启动的协程:异常在调用await()时抛出 
 - 直接使用 
 - 
异常捕获方式:
try/catch块包裹挂起函数调用- 使用 
CoroutineExceptionHandler捕获未处理异常 
 
            
            
              kotlin
              
              
            
          
          val exceptionHandler = CoroutineExceptionHandler { _, exception ->
    println("Caught exception: $exception")
}
launch(Dispatchers.Main + exceptionHandler) {
    riskyOperation() // 异常会被handler捕获
}
// 或使用try/catch
launch {
    try {
        riskyOperation()
    } catch (e: Exception) {
        // 处理异常
    }
}
        协程作用域的错误处理策略
不同作用域对异常的处理方式不同:
- 普通作用域:一个子协程异常会取消整个作用域
 - Supervisor作用域:子协程异常不会传播给其他子协程
 
            
            
              kotlin
              
              
            
          
          // 异常隔离示例
supervisorScope {
    launch {
        // 此协程的异常不会影响其他协程
        throw RuntimeException("Failed in child")
    }
    launch {
        // 不受上述异常影响
        delay(1000)
        println("This still executes")
    }
}
        - 全局异常处理:为应用定义全局异常处理器,捕获未处理异常
 
协程测试框架与实践技巧
使用官方提供的 kotlinx-coroutines-test 库测试协程代码。
- 测试调度器 :使用 
TestDispatcher控制时间和线程 
            
            
              kotlin
              
              
            
          
          @get:Rule
val coroutineRule = MainCoroutineRule() // 使用TestDispatcher
@Test
fun testDataLoading() = runTest {
    // 测试代码
    val viewModel = MyViewModel(repository)
    viewModel.loadData()
    advanceUntilIdle() // 执行所有挂起函数
    assertEquals(/* 验证结果 */)
}
        - 测试技巧 :
- 使用 
runTest函数标记测试函数 - 使用 
advanceTimeBy控制时间流逝 - 使用 
UnconfinedTestDispatcher立即执行协程 - 验证协程取消和异常情况
 
 - 使用