协程是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) {
        // 处理超时
    }
}

最佳实践

  1. 避免使用GlobalScope - 使用自定义的CoroutineScope

  2. 合理选择调度器 - 根据任务类型选择适当的Dispatcher

  3. 处理取消 - 协程取消时应释放资源

  4. 避免阻塞操作 - 使用挂起函数替代阻塞调用

  5. 使用结构化并发 - 确保协程有明确的生命周期

协程是Kotlin异步编程的强大工具,合理使用可以大大简化并发代码的编写和维护。

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐