返回正文
Kotlin 协程
协程是 Kotlin 异步编程的核心,也是现在 Android 开发必学的技能。这篇文章帮你从零搞懂协程的核心概念和实战用法。
协程是什么
协程是一种「轻量级线程」,让异步代码看起来像同步代码。
对比传统异步方案:
kotlin
// Java 回调地狱
api.getUser(object : Callback {
override fun onSuccess(user: User) {
api.getOrders(user.id, object : Callback {
override fun onSuccess(orders: List<Order>) {
api.getProducts(orders, object : Callback {
override fun onSuccess(products: List<Product>) {
// 终于拿到了
}
})
}
})
}
})
// Kotlin 协程:像写同步代码一样
suspend fun loadData() {
val user = api.getUser()
val orders = api.getOrders(user.id)
val products = api.getProducts(orders)
// 搞定!
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
核心概念
挂起函数
suspend 修饰的函数可以「暂停」执行,不阻塞线程:
kotlin
// 挂起函数
suspend fun fetchUser(): User {
delay(1000) // 模拟网络请求,暂停1秒
return User("张三", 25)
}
// 调用
suspend fun loadData() {
val user = fetchUser() // 这里会暂停,但不会卡住线程
println(user.name) // 1秒后继续执行
}1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
注意:suspend 函数只能在协程或其他挂起函数中调用。
CoroutineScope:协程作用域
协程必须在作用域中启动,作用域决定协程的生命周期:
kotlin
// Android 中常用的作用域
viewModelScope.launch { } // ViewModel 销毁时取消
lifecycleScope.launch { } // Activity/Fragment 销毁时取消1
2
3
2
3
协程启动方式
launch:fire-and-forget
java
// 最常用:不阻塞,异步执行,不需要返回值
viewModelScope.launch {
val data = api.fetchData()
_uiState.value = data
}1
2
3
4
5
2
3
4
5
返回值:Job,可以用来取消协程。
kotlin
val job = viewModelScope.launch {
// 协程逻辑
}
// 取消协程
job.cancel()
job.join() // 等待取消完成1
2
3
4
5
6
7
2
3
4
5
6
7
async:返回结果
kotlin
// 需要返回值时用 async
viewModelScope.launch {
val user = async { api.fetchUser() }
val orders = async { api.fetchOrders() }
// await() 获取结果
println(user.await().name)
println(orders.await())
}1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
runBlocking:测试用
kotlin
fun main() = runBlocking {
// 阻塞当前线程
val result = async { fetchData() }
println(result.await())
}1
2
3
4
5
2
3
4
5
警告:不要在 Android 主线程使用 runBlocking,会导致 ANR。
并行请求:async 的价值
串行 vs 并行
kotlin
// ❌ 串行:总耗时 = 1000 + 800 + 600 = 2400ms
viewModelScope.launch {
val user = api.fetchUser() // 1000ms
val orders = api.fetchOrders() // 800ms
val banners = api.fetchBanners() // 600ms
}
// ✅ 并行:总耗时 ≈ 1000ms(最慢那个)
viewModelScope.launch {
val userDeferred = async { api.fetchUser() }
val ordersDeferred = async { api.fetchOrders() }
val bannersDeferred = async { api.fetchBanners() }
val user = userDeferred.await()
val orders = ordersDeferred.await()
val banners = bannersDeferred.await()
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
awaitAll:批量等待
kotlin
viewModelScope.launch {
val (user, orders, banners) = awaitAll(
async { api.fetchUser() },
async { api.fetchOrders() },
async { api.fetchBanners() }
)
}1
2
3
4
5
6
7
2
3
4
5
6
7
结构化并发
协程的「结构化并发」特性保证:
- 子协程异常会导致父协程取消
- 父协程取消会自动取消所有子协程
- 页面销毁时自动清理所有协程
kotlin
viewModelScope.launch { // 父协程
val user = async { api.fetchUser() }
val orders = async { api.fetchOrders() }
// 如果 fetchOrders 抛异常,fetchUser 也会被取消
}1
2
3
4
5
6
2
3
4
5
6
异常处理
try-catch
kotlin
viewModelScope.launch {
try {
val data = api.fetchData()
_uiState.value = UiState.Success(data)
} catch (e: Exception) {
_uiState.value = UiState.Error(e.message ?: "未知错误")
}
}1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
CoroutineExceptionHandler
kotlin
val handler = CoroutineExceptionHandler { _, exception ->
Log.e("Coroutine", "未捕获异常", exception)
}
viewModelScope.launch(handler) {
// 协程代码
}1
2
3
4
5
6
7
2
3
4
5
6
7
Dispatchers:线程切换
kotlin
viewModelScope.launch {
// 默认在主线程
withContext(Dispatchers.IO) {
// 切换到 IO 线程执行耗时操作
val data = api.fetchData()
}
// 自动切回主线程,继续执行
_uiState.value = data
}1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
常用 Dispatchers:
| Dispatcher | 用途 |
|---|---|
| Main | 主线程,更新 UI |
| IO | IO 操作,网络、数据库 |
| Default | CPU 密集型计算 |
实战模板
网络请求标准模板
kotlin
class HomeViewModel : ViewModel() {
sealed class UiState {
object Loading : UiState()
data class Success(val data: List<String>) : UiState()
data class Error(val msg: String) : UiState()
}
private val _uiState = MutableStateFlow<UiState>(UiState.Loading)
val uiState: StateFlow<UiState> = _uiState.asStateFlow()
fun loadData() {
viewModelScope.launch {
_uiState.value = UiState.Loading
try {
val data = withContext(Dispatchers.IO) {
api.fetchData()
}
_uiState.value = UiState.Success(data)
} catch (e: Exception) {
_uiState.value = UiState.Error(e.message ?: "加载失败")
}
}
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
并行请求模板
kotlin
fun loadHomePage() {
viewModelScope.launch {
try {
val (user, orders, banners) = awaitAll(
async(Dispatchers.IO) { api.fetchUser() },
async(Dispatchers.IO) { api.fetchOrders() },
async(Dispatchers.IO) { api.fetchBanners() }
)
_uiState.value = UiState.Success(user, orders, banners)
} catch (e: Exception) {
_uiState.value = UiState.Error(e.message ?: "加载失败")
}
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
常见问题
Q:launch 和 async 怎么选?
A:
- 不需要返回值 →
launch - 需要返回值,或要并行执行 →
async
Q:协程会内存泄漏吗?
A:使用 viewModelScope 或 lifecycleScope 不会,它们会在页面销毁时自动取消。不要用 GlobalScope。
Q:协程和 RxJava 怎么选?
A:新项目推荐协程,语法更简洁,生命周期管理更安全。老项目如果 RxJava 用得顺手,继续用也没问题。
总结
| 概念 | 说明 |
|---|---|
| suspend | 挂起函数,可在不阻塞线程的情况下暂停 |
| launch | 启动协程,不返回结果 |
| async | 启动协程,返回 Deferred,可获取结果 |
| await() | 等待并获取 async 的结果 |
| awaitAll() | 等待多个 async 完成 |
| Dispatchers.IO | IO 线程,耗时操作 |
| Dispatchers.Main | 主线程,UI 操作 |
V 0.13.13 |
基于 MIT Licensed版权所有 © 2009- 2026 CMONO.NET
本站访客数
--次 本站总访问量
--人次 