协程快速上手
Kotlin 协程不仅是轻量级线程,更是一种非阻塞式的编程范式。在开始深入底层原理之前,我们需要掌握如何在实际项目中正确地启动和管理它们。
环境准备
协程核心库分为两个部分。对于大多数 Android 项目,需要引入以下依赖:
kotlin
dependencies {
// 协程核心库(包含标准 API、Flow 等)
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0")
// Android 平台支持(提供 Dispatchers.Main 等)
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.9.0")
}你的第一个协程
启动协程的最基本方式是使用 launch 或 async 构建器。
kotlin
// 在一个作用域中启动协程
fun main() = runBlocking { // runBlocking 常用于 main 函数,阻塞直到内部执行完
launch { // 启动一个新协程
delay(1000L) // 非阻塞等待 1 秒
println("World!")
}
println("Hello,")
}kotlin
fun main() = runBlocking {
val deferred = async {
delay(1000L)
"Kotlin Coroutines"
}
// 模拟其他工作...
println("Waiting for result...")
// 调用 await() 获取结果(会挂起直到结果就绪)
println("Received: ${deferred.await()}")
}协程的三大支柱
要玩转协程,必须理解以下三个核心概念:
1. 作用域 (CoroutineScope)
协程必须依附于一个作用域,作用域决定了协程的生命周期。
- UI 开发:在 Android 中常用
lifecycleScope或viewModelScope,它们会在组件销毁时自动取消协程。 - 通用开发:使用
coroutineScope或MainScope。
2. 上下文 (CoroutineContext)
它是协程的“数据包”,包含了协程的名字、Job 以及最关键的调度器。
3. 调度器 (Dispatchers)
调度器决定了协程在哪个线程上运行:
Dispatchers.Main:UI 线程。用于更新视图。Dispatchers.IO:IO 线程池。用于网络请求、文件读写。Dispatchers.Default:计算密集型线程池。用于复杂算法或 JSON 解析。
实战:线程切换与异步请求
在 Android 开发中,最常见的场景是:在后台请求数据,然后切回主线程显示。
kotlin
class MyViewModel : ViewModel() {
fun loadUserData() {
// viewModelScope 自动在 ViewModel 清理时取消协程
viewModelScope.launch {
try {
// 1. 在 IO 线程执行耗时操作
val user = withContext(Dispatchers.IO) {
api.fetchUser() // 挂起函数
}
// 2. 自动恢复到主线程,安全更新 UI
userNameView.text = user.name
} catch (e: Exception) {
// 3. 异常处理
showError(e)
}
}
}
}挂起函数:协程的原子
如果你想把一段异步代码抽离出来,必须使用 suspend 关键字。
kotlin
// 挂起函数只能在协程或其他挂起函数中调用
suspend fun fetchFromNetwork(): String {
return withContext(Dispatchers.IO) {
// 执行实际网络操作
"Network Result"
}
}编写准则
挂起函数应该是主线程安全的。这意味着即使在主线程调用它,它也应该通过 withContext 内部切换线程,而不应该阻塞主线程。