前置知识: Kotlin

协程基础

00:00
3 min Intermediate 2026/6/14

Kotlin协程入门

概述

Kotlin 协程轻量级的并发方案通过挂起函数实现非阻塞异步编程。协程的创建和调度成本远低于线程,可以在单线程上运行数万个协程。本文介绍协程的基础概念、启动方式和作用域管理。

基础概念

协程核心术语

术语说明
CoroutineScope协程作用控制协程的生命周期
launch启动协程,返回 Job(不返回结果)
async启动协程,返回 Deferred(可获取结果)
suspend挂起函数标记,表示可暂停执行
Dispatcher调度器,决定协程在哪个线程
Job协程的句柄,可取消和等待

调度器类型

调度器用途
Dispatchers.DefaultCPU 密集型任务
Dispatchers.IOI/O 操作
Dispatchers.MainUI 线程(Android)
Dispatchers.Unconfined限定线程

快速上手

启动协程

import kotlinx.coroutines.*

// launch — 启动协程(不返回结果)
runBlocking {
    val job = launch {
        delay(1000)
        println("协程执行完成")
    }
    job.join() // 等待协程完成
}

// async — 启动协程(返回结果)
runBlocking {
    val deferred = async { fetchUser() }
    val user = deferred.await() // 获取结果
    println(user)
}

协程作用域

// viewModelScope — ViewModel 销毁时自动取消
class MyViewModel : ViewModel() {
    fun loadData() {
        viewModelScope.launch {
            val data = repository.fetch()
            _state.value = data
        }
    }
}

// lifecycleScope — Activity/Fragment 生命周期绑定
class MyActivity : AppCompatActivity() {
    fun loadData() {
        lifecycleScope.launch {
            val data = repository.fetch()
            updateUI(data)
        }
    }
}

// coroutineScope — 创建临时作用域,所有子协程完成后才结束
suspend fun fetchAll() = coroutineScope {
    val user = async { fetchUser() }
    val orders = async { fetchOrders() }
    Pair(user.await(), orders.await())
}

详细用法

挂起函数

// suspend 修饰的函数可以在协程中调用,也可以调用其他挂起函数
suspend fun fetchUser(): User {
    delay(1000) // 模拟网络请求(挂起点)
    return User("张三", 25)
}

// 挂起函数是安全的,不会阻塞线程
suspend fun loadData() {
    val user = fetchUser()       // 挂起,不阻塞
    val orders = fetchOrders()   // 挂起,不阻塞
    updateUI(user, orders)
}

// withContext 切换调度器
suspend fun saveData(data: Data) {
    // 在 IO 调度器上执行,完成后切回原调度器
    val result = withContext(Dispatchers.IO) {
        database.save(data)
    }
    showResult(result)
}

协程取消与超时

// 取消协程
val job = launch {
    repeat(1000) {
        // 检查取消状态
        ensureActive()
        // 或者使用可取消的挂起函数
        delay(100)
        println("处理: $it")
    }
}
delay(500)
job.cancel() // 取消协程
job.join()   // 等待取消完成
// 简写:job.cancelAndJoin()

// 超时控制
try {
    withTimeout(3000) {
        fetchData() // 超过3秒抛出 TimeoutCancellationException
    }
} catch (e: TimeoutCancellationException) {
    println("请求超时")
}

// 超时返回 null 而非抛异常
val result = withTimeoutOrNull(3000) {
    fetchData()
}
if (result == null) {
    println("请求超时")
}

异常处理

// SupervisorJob — 子协程失败不影响其他子协程
val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main)

// supervisorScope — 临时作用域
supervisorScope {
    launch {
        throw RuntimeException("子协程1失败") // 不影响子协程2
    }
    launch {
        delay(100)
        println("子协程2正常执行")
    }
}

// CoroutineExceptionHandler — 全局异常处理
val handler = CoroutineExceptionHandler { _, exception ->
    println("捕获异常: ${exception.message}")
}
scope.launch(handler) {
    throw RuntimeException("出错了")
}

常见场景

并发请求

// 使用 async 并发执行多个请求
suspend fun loadPage(): PageData = coroutineScope {
    val user = async { apiService.getUser() }
    val posts = async { apiService.getPosts() }
    val notifications = async { apiService.getNotifications() }
    PageData(user.await(), posts.await(), notifications.await())
}

重试机制

// 简单重试
suspend fun <T> retry(
    times: Int = 3,
    delay: Long = 1000,
    block: suspend () -> T
): T {
    repeat(times) { attempt ->
        try {
            return block()
        } catch (e: Exception) {
            if (attempt == times - 1) throw e
            delay(delay * (attempt + 1)) // 递增延迟
        }
    }
    throw IllegalStateException("不应到达此处")
}

// 使用
val data = retry(times = 3) { apiService.fetchData() }

注意事项

  • 协程不是线程协程可以在线程切换,挂起时不占用线程
  • 不要使用 GlobalScope,它创建的协程生命周期与应用相同,容易泄漏
  • 挂起函数只能在协程其他挂起函数调用
  • CPU 密集型任务使用 Dispatchers.Default,I/O 操作使用 Dispatchers.IO
  • 协程取消是协作式的,需要在循环或长时间操作中检查 ensureActive()
  • 不要在挂起函数中使用 Thread.sleep,应使用 delay

进阶用法

Channel 通信

// 使用 Channel 在协程间传递数据
val channel = Channel<Int>()

// 生产者
launch {
    for (i in 1..10) {
        channel.send(i)
    }
    channel.close()
}

// 消费者
launch {
    for (item in channel) {
        println("收到: $item")
    }
}

// produce 构建器
fun CoroutineScope.numberProducer() = produce {
    for (i in 1..10) {
        send(i)
    }
}

Select 等待多个操作

// select 同时等待多个挂起操作,哪个先完成就处理哪个
suspend fun fetchFastest(): String = coroutineScope {
    val local = async { cache.get() }
    val remote = async { apiService.fetch() }
    select {
        local.onAwait { "缓存: $it" }
        remote.onAwait { "网络: $it" }
    }
}

知识检测

学习进度

-- 已学文档
--% 知识覆盖率

学习推荐

专注模式