Kotlin协程
外观
- Kotlin协程
Kotlin协程是Kotlin语言提供的一种轻量级并发编程解决方案,它简化了异步编程和并发操作的管理。协程允许开发者以顺序的方式编写异步代码,同时保持高效的资源利用。
- 概述
Kotlin协程基于以下核心概念:
- **轻量级线程**:比传统线程更高效,可创建数千个协程而不会导致性能问题
- **挂起函数**:使用`suspend`关键字标记,可以在不阻塞线程的情况下暂停执行
- **结构化并发**:提供明确的生命周期管理和错误传播机制
- **协程上下文**:定义协程执行的环境,包括调度器、异常处理器等
- 基本用法
- 启动协程
Kotlin提供了几种启动协程的方式:
// 使用GlobalScope启动协程(不推荐在生产代码中使用)
GlobalScope.launch {
// 协程体
fetchData()
}
// 推荐的方式:使用CoroutineScope
val scope = CoroutineScope(Dispatchers.Main)
scope.launch {
// 在主线程执行的协程
val result = fetchData()
updateUI(result)
}
- 挂起函数
挂起函数是协程的核心构建块:
suspend fun fetchData(): String {
return withContext(Dispatchers.IO) {
// 模拟网络请求
delay(1000) // 非阻塞延迟
"数据加载完成"
}
}
- 协程调度器
Kotlin提供了几种预定义的调度器:
- **Dispatchers.Main**:Android主线程
- **Dispatchers.IO**:适合I/O密集型操作
- **Dispatchers.Default**:适合CPU密集型操作
- **Dispatchers.Unconfined**:不限定特定线程
- 结构化并发
Kotlin协程通过结构化并发管理协程生命周期:
class MyActivity : AppCompatActivity(), CoroutineScope by MainScope() {
override fun onDestroy() {
super.onDestroy()
cancel() // 取消所有子协程
}
fun loadData() {
launch {
try {
val data = fetchData()
showData(data)
} catch (e: Exception) {
showError(e)
}
}
}
}
- 协程与Android应用
在Android开发中,协程常用于以下场景:
1. **网络请求**:替代回调或RxJava 2. **数据库操作**:Room数据库支持协程 3. **UI更新**:在主线程安全地更新UI 4. **并行任务**:使用`async/await`模式
示例:结合ViewModel使用协程
class MyViewModel : ViewModel() {
private val repository = DataRepository()
val data = liveData {
emit(Resource.loading())
try {
emit(Resource.success(repository.fetchData()))
} catch (e: Exception) {
emit(Resource.error(e))
}
}
}
- 高级特性
- 协程上下文
可以自定义协程上下文:
val customDispatcher = Executors.newFixedThreadPool(4).asCoroutineDispatcher()
val exceptionHandler = CoroutineExceptionHandler { _, exception ->
println("协程异常: $exception")
}
val customContext = customDispatcher + exceptionHandler
- 通道(Channel)
协程间的通信机制:
val channel = Channel<Int>()
launch {
for (x in 1..5) channel.send(x)
channel.close()
}
launch {
for (y in channel) println(y)
}
- 流(Flow)
异步数据流处理:
fun fetchItems(): Flow<Item> = flow {
for (i in 1..10) {
delay(100)
emit(Item(i))
}
}
launch {
fetchItems()
.filter { it.id % 2 == 0 }
.collect { item -> println(item) }
}
- 性能优势
Kotlin协程相比传统线程的优势:
- **内存占用低**:协程栈可复用,创建成本低
- **调度高效**:协程切换不涉及操作系统线程切换
- **资源可控**:通过调度器限制并发数量
- **错误处理**:结构化并发简化错误传播
- 常见问题
- 协程与线程的区别
| 特性 | 协程 | 线程 | |------|------|------| | 创建成本 | 低 | 高 | | 切换开销 | 小 | 大 | | 并发数量 | 数千 | 数百 | | 调度方式 | 协作式 | 抢占式 | | 内存占用 | 几十KB | 几MB |
- 协程泄漏
协程泄漏常见原因:
- 忘记取消不再需要的协程
- 在已销毁的Activity/Fragment中继续运行协程
- 持有外部对象的强引用
解决方案:
- 使用结构化并发
- 实现CoroutineScope接口
- 使用viewModelScope或lifecycleScope
- 最佳实践
1. **避免GlobalScope**:使用自定义作用域 2. **正确处理异常**:使用CoroutineExceptionHandler 3. **合理选择调度器**:根据任务类型选择 4. **测试协程代码**:使用TestCoroutineDispatcher 5. **结合LiveData使用**:使用liveData构建器
- 相关库
- **kotlinx.coroutines**:官方协程库
- **Room**:支持协程的数据库库
- **Retrofit**:支持协程的网络库
- **WorkManager**:支持协程的后台任务调度
- 参见