kotlin协程
一、基础概念层(初级)
1. 什么是协程?
"协程是 Kotlin 提供的一种轻量级线程管理方案,它本质上是一个线程框架,通过挂起(suspend)而非阻塞的方式实现异步操作。与线程相比,协程的上下文切换成本更低,一个应用程序可以轻松创建上万个协程而不会导致资源耗尽。"
2. 协程的基本使用
kotlin
// 最简单的协程示例
GlobalScope.launch { // 启动协程
delay(1000) // 非阻塞延迟
println("Hello") // 在后台线程执行
}
3. 关键组件解释
-
CoroutineScope:协程作用域,管理协程生命周期
-
Dispatcher:决定协程运行的线程
-
suspend:标记可挂起函数
-
Job:协程的任务句柄
-
Deferred:带有返回值的Job
二、中级原理层
1. 协程为什么轻量?
"协程的轻量体现在三个方面:
-
栈帧更小:协程挂起时只保存必要的局部变量
-
调度更高效:由程序控制切换而非操作系统
-
内存占用低:一个协程实例仅需几十字节,而线程需要MB级内存"
2. 挂起函数的原理
"挂起函数通过 CPS(Continuation Passing Style) 转换实现。编译器会将 suspend 函数转换为带有 Continuation 参数的回调形式,在挂起点插入状态标记,恢复时根据状态跳转到对应位置继续执行。"
3. 结构化并发
kotlin
// 结构化并发示例
coroutineScope {
launch { task1() }
async { task2() }
} // 会自动等待所有子协程完成
"结构化并发确保:
-
父协程取消时,所有子协程都会被取消
-
子协程异常会传播给父协程
-
父协程会等待所有子协程完成"
三、高级应用层
1. 协程性能优化
kotlin
// 优化密集型计算
val result = withContext(Dispatchers.Default) {
// 使用Default调度器进行CPU密集型计算
computeHeavyTask()
}
// 限制并发数
val semaphore = Semaphore(5)
repeat(100) {
launch {
semaphore.withPermit {
// 最多5个并发
fetchData()
}
}
}
2. 复杂异常处理
kotlin
// 高级异常处理方案
val handler = CoroutineExceptionHandler { _, e ->
// 全局异常处理
}
val scope = CoroutineScope(SupervisorJob() + handler)
scope.launch {
try {
val data = async(Dispatchers.IO) { fetchData() }.await()
process(data)
} catch (e: IOException) {
// 特定异常处理
}
}
3. 协程测试
kotlin
// 高级异常处理方案
val handler = CoroutineExceptionHandler { _, e ->
// 全局异常处理
}
val scope = CoroutineScope(SupervisorJob() + handler)
scope.launch {
try {
val data = async(Dispatchers.IO) { fetchData() }.await()
process(data)
} catch (e: IOException) {
// 特定异常处理
}
}
四、架构设计层
1. 在MVVM中的最佳实践
kotlin
class MyViewModel : ViewModel() {
private val _data = MutableStateFlow<String?>(null)
val data: StateFlow<String?> = _data
fun loadData() {
viewModelScope.launch {
_data.value = "Loading..."
try {
_data.value = repository.fetchData()
} catch (e: Exception) {
_data.value = "Error: ${e.message}"
}
}
}
}
2. 与其他技术的结合
-
Retrofit:直接使用 suspend 函数
-
Room:DAO 支持 suspend 函数
-
WorkManager:使用 CoroutineWorker
-
Flow:与协程天然集成
五、深度原理层
1. 协程调度器实现
"Dispatchers.Default 使用 ForkJoinPool 实现,Dispatchers.IO 在 Default 基础上增加了阻塞感知和动态扩容机制。当线程阻塞时,IO 调度器会创建新线程避免饥饿。"
2. 协程与线程的对比
特性 | 协程 | 线程 |
---|---|---|
创建成本 | 几十字节 | 1MB+栈内存 |
切换成本 | 纳秒级 | 微秒级 |
并发数量 | 轻松数万 | 数百个 |
调度方式 | 协作式 | 抢占式 |
六、常见问题应对
Q: 协程会取代RxJava吗?
"A: 在大多数场景下,协程配合Flow可以替代RxJava。但对于复杂的响应式流处理,RxJava仍有一定优势。Google官方推荐新项目使用协程,老项目可以逐步迁移。"
Q: 如何处理协程的内存泄漏? "
A: 主要预防措施包括:
1. 使用viewModelScope/lifecycleScope自动绑定生命周期
2. 避免在全局Scope启动长周期协程
3. 及时取消不再需要的协程
4. 使用WeakReference持有上下文引用"
Q: 如何调试协程? "
A: 可以使用以下方法:
1. 添加CoroutineName进行标识
2. 使用Debug模式下的协程调试器
3. 添加日志记录协程ID
4. 使用kotlinx-coroutines-debug模块"