协程的组合与作用域
1. 协程的组合
在复杂的异步编程场景中,我们经常需要组合多个协程来完成业务逻辑。Kotlin 协程提供了多种方式来实现协程的组合:
1.1 顺序组合
suspend fun getUserData(): UserData {
val user = fetchUser() // 挂起函数
val profile = fetchProfile(user.id) // 挂起函数
return UserData(user, profile)
}
1.2 并行组合
使用 async 实现并行执行:
suspend fun getCombinedData(): CombinedData {
val data1 = async { fetchData1() }
val data2 = async { fetchData2() }
return CombinedData(data1.await(), data2.await())
}
1.3 结构化并发
Kotlin 协程通过 coroutineScope 构建器实现结构化并发:
suspend fun loadData() = coroutineScope {
val data1 = async { fetchData1() }
val data2 = async { fetchData2() }
DataWrapper(data1.await(), data2.await())
}
2. 协程作用域
协程作用域(CoroutineScope)是协程执行的环境,它定义了协程的生命周期和上下文。
2.1 作用域的类型
GlobalScope:
- 生命周期与应用程序相同
- 通常不推荐在生产代码中使用
自定义作用域:
class MyActivity : CoroutineScope { private lateinit var job: Job override val coroutineContext: CoroutineContext get() = Dispatchers.Main + job fun onCreate() { job = Job() } fun onDestroy() { job.cancel() } }ViewModelScope(Android):
viewModelScope.launch { // 协程代码 }
2.2 作用域的重要性
生命周期管理:
- 自动取消所有子协程
- 防止内存泄漏
上下文传播:
- 子协程继承父协程的上下文
- 可以覆盖特定元素
2.3 创建自定义作用域
val myScope = CoroutineScope(Dispatchers.IO + SupervisorJob())
// 使用
myScope.launch {
// 协程代码
}
// 取消
myScope.cancel()
3. 最佳实践
- 避免使用 GlobalScope
- 为每个有生命周期的组件创建作用域
- 使用 SupervisorJob 处理独立子协程的失败
- 合理选择调度器(Dispatchers)
- 及时清理不再需要的协程
4. 示例:组合多个协程
suspend fun fetchUserAndPosts(userId: String): UserWithPosts = coroutineScope {
val userDeferred = async { fetchUser(userId) }
val postsDeferred = async { fetchPosts(userId) }
UserWithPosts(
user = userDeferred.await(),
posts = postsDeferred.await()
)
}
在这个例子中,coroutineScope 确保了两个异步操作要么都成功完成,要么在其中一个失败时全部取消。
