十一、Kotlin 协程详解
·
协程是Kotlin中用于异步编程的轻量级解决方案,它提供了一种更简单、更直观的方式来处理异步操作,避免了传统回调地狱的问题。
基本概念
什么是协程
协程(Coroutine)是可挂起计算的实例,它在概念上类似于线程,但有以下特点:
-
轻量级:可以在单个线程中运行多个协程
-
可挂起:可以在不阻塞线程的情况下挂起执行
-
结构化并发:提供了取消和错误传播的机制
协程 vs 线程
| 特性 | 协程 | 线程 |
|---|---|---|
| 创建成本 | 非常低 | 较高 |
| 上下文切换 | 由程序员控制 | 由系统调度 |
| 阻塞 | 不阻塞线程 | 可能阻塞线程 |
| 数量 | 可创建数千个 | 数量有限 |
核心组件
1. 协程构建器
Kotlin提供了几种启动协程的方式:
// 1. launch - 启动一个不返回结果的协程
val job = GlobalScope.launch {
// 协程体
}
// 2. async - 启动一个返回Deferred结果的协程
val deferred = GlobalScope.async {
// 返回结果
"Result"
}
// 3. runBlocking - 阻塞当前线程直到协程执行完毕
runBlocking {
// 协程体
}
2. 协程作用域 (CoroutineScope)
协程作用域定义了协程的生命周期范围:
// 自定义作用域
val scope = CoroutineScope(Dispatchers.Main + Job())
scope.launch {
// 在此作用域内启动的协程
}
// 取消作用域下的所有协程
scope.cancel()
3. 调度器 (Dispatchers)
决定协程在哪个线程或线程池上执行:
-
Dispatchers.Main- Android主线程 -
Dispatchers.IO- 适合IO密集型操作 -
Dispatchers.Default- 适合CPU密集型操作 -
Dispatchers.Unconfined- 不限定特定线程
GlobalScope.launch(Dispatchers.IO) {
// 在IO线程执行
val result = withContext(Dispatchers.Default) {
// 切换到Default线程执行计算
compute()
}
// 自动切换回IO线程
}
4. 挂起函数 (suspend)
挂起函数是协程的核心概念,使用suspend关键字标记:
suspend fun fetchUserData(): User {
return withContext(Dispatchers.IO) {
// 模拟网络请求
delay(1000)
User("John")
}
}
协程的生命周期
协程有以下几种状态:
-
New - 新创建
-
Active - 正在运行
-
Completing - 正在完成
-
Completed - 已完成
-
Cancelling - 正在取消
-
Cancelled - 已取消
可以通过Job对象管理协程生命周期:
val job = launch {
// 协程体
}
// 取消协程
job.cancel()
// 等待协程完成
job.join()
// 取消并等待
job.cancelAndJoin()
异常处理
协程提供了几种异常处理机制:
1. try-catch
launch {
try {
fetchData()
} catch (e: Exception) {
// 处理异常
}
}
2. CoroutineExceptionHandler
val handler = CoroutineExceptionHandler { _, exception ->
println("Caught $exception")
}
val scope = CoroutineScope(Job() + handler)
scope.launch {
throw RuntimeException("Failed!")
}
3. SupervisorJob
允许子协程独立失败而不影响其他子协程:
val scope = CoroutineScope(SupervisorJob())
scope.launch {
// 子协程1
}
scope.launch {
// 子协程2 - 失败不会影响子协程1
throw RuntimeException()
}
结构化并发
Kotlin协程的重要特性,确保协程之间的关系和生命周期管理:
suspend fun fetchAndShowUser() {
coroutineScope { // 创建一个新的作用域
val user = async { fetchUser() }
val posts = async { fetchPosts() }
showUser(user.await(), posts.await())
}
// 所有子协程完成后才会继续
}
实际应用示例
1. 顺序执行异步操作
suspend fun loadData() {
val user = getUser() // 挂起函数
val friends = getFriends() // 在前一个完成后执行
show(user, friends)
}
2. 并发执行异步操作
suspend fun loadDataConcurrently() = coroutineScope {
val user = async { getUser() }
val friends = async { getFriends() }
show(user.await(), friends.await())
}
3. 超时处理
suspend fun fetchWithTimeout() {
try {
val result = withTimeout(1000) {
fetchData() // 必须在1秒内完成
}
} catch (e: TimeoutCancellationException) {
// 处理超时
}
}
最佳实践
-
避免使用GlobalScope - 使用自定义的CoroutineScope
-
合理选择调度器 - 根据任务类型选择适当的Dispatcher
-
处理取消 - 协程取消时应释放资源
-
避免阻塞操作 - 使用挂起函数替代阻塞调用
-
使用结构化并发 - 确保协程有明确的生命周期
协程是Kotlin异步编程的强大工具,合理使用可以大大简化并发代码的编写和维护。
更多推荐

所有评论(0)