一、协程基础使用
1. 协程的三种创建方式
(1) launch
- 启动后台作业
val job = CoroutineScope(Dispatchers.IO).launch {// 后台操作delay(1000)println("任务完成 ${Thread.currentThread().name}")// 输出:任务完成 DefaultDispatcher-worker-1
}
job.join() // 等待完成
(2) async
- 启动带结果作业
val deferred = CoroutineScope(Dispatchers.Default).async {delay(500)"计算结果"
}// 获取结果(会挂起协程)
val result = deferred.await()
println(result) // 输出:计算结果
(3) runBlocking
- 阻塞线程启动
runBlocking {launch {delay(100)println("内部协程")}println("外部协程")
}
// 输出:
// 外部协程
// 内部协程
2. 调度器选择(四种核心类型)
调度器 | 用途 | 示例 |
---|---|---|
Dispatchers.Main | UI线程操作 | textView.text = "更新" |
Dispatchers.IO | 网络/文件IO | Retrofit API调用 |
Dispatchers.Default | CPU密集型计算 | 复杂算法、数据处理 |
Dispatchers.Unconfined | 特殊场景 | 不限制线程,慎用 |
CoroutineScope(Dispatchers.Main).launch {val data = withContext(Dispatchers.IO) { // 切换到IO线程fetchFromNetwork() // 网络请求}updateUI(data) // 回到主线程更新UI
}
3. 结构化并发(生命周期管理)
class MyActivity : AppCompatActivity() {// 绑定Activity生命周期private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main)override fun onCreate() {scope.launch {loadData()}}override fun onDestroy() {scope.cancel() // 取消所有协程}suspend fun loadData() {// ...}
}
二、协程间通信
1. Channel - 点对点通信
(1) 基础使用
val channel = Channel<Int>()// 生产者
launch {repeat(5) {channel.send(it)delay(100)}channel.close()
}// 消费者
launch {for (value in channel) {println("收到: $value")}
}
// 输出: 收到: 0, 1, 2, 3, 4
(2) Channel类型对比
类型 | 特性 | 适用场景 |
---|---|---|
RENDEZVOUS | 无缓冲(默认) | 严格同步 |
BUFFERED | 固定大小缓冲 | 允许生产者领先 |
CONFLATED | 保留最新值 | 状态更新 |
UNLIMITED | 无限缓冲 | 大批量数据 |
// 创建不同类型Channel
val rendezvous = Channel<Int>() // 无缓冲
val buffered = Channel<Int>(10) // 缓冲区大小10
val conflated = Channel<Int>(Channel.CONFLATED) // 只保留最新
2. Flow - 异步数据流
(1) 冷流 vs 热流
特性 | Cold Flow | Hot Flow (SharedFlow) |
---|---|---|
启动 | 收集时启动 | 独立于收集者 |
数据 | 每次收集重新发射 | 共享数据流 |
示例 | 数据库查询 | 实时位置更新 |
(2) Flow基本使用
fun dataFlow(): Flow<Int> = flow {repeat(5) {delay(100)emit(it) // 发射数据}
}// 收集数据
CoroutineScope(Dispatchers.Main).launch {dataFlow().filter { it % 2 == 0 } // 过滤偶数.map { it * 2 } // 转换.collect { value -> // 收集println("值: $value")}
}
// 输出: 值: 0, 值: 4, 值: 8
3. SharedFlow & StateFlow - 状态管理
(1) SharedFlow - 事件广播
// 创建共享流(带重播1个事件)
val sharedFlow = MutableSharedFlow<Event>(replay = 1)// 发送事件
launch {sharedFlow.emit(Event.UPDATE)
}// 多个接收者
repeat(3) { i ->launch {sharedFlow.collect { event ->println("接收者$i: $event")}}
}
(2) StateFlow - 状态容器
// 创建状态容器(初始值0)
val stateFlow = MutableStateFlow(0)// 更新状态
launch {repeat(10) {delay(200)stateFlow.value = it}
}// 监听状态变化
launch {stateFlow.collect { state ->println("当前状态: $state")}
}
// 输出: 当前状态: 0,1,2,...,9
4. Select - 多路复用
val chan1 = Channel<String>()
val chan2 = Channel<String>()launch { delay(50); chan1.send("A") }
launch { delay(30); chan2.send("B") }// 选择最先到达的消息
val result = select<String> {chan1.onReceive { it }chan2.onReceive { it }
}println("结果: $result") // 输出: 结果: B
三、常见问题与答案
问题1:launch和async有什么区别?
标准回答:
-
launch
启动不需要返回结果的作业,返回Job
对象用于控制生命周期 -
async
启动需要返回结果的作业,返回Deferred
对象,通过await()
获取结果 -
async
通常用于并行任务,launch
用于后台执行
问题2:Channel和Flow有什么区别?
对比回答:
特性 | Channel | Flow |
---|---|---|
数据模式 | 点对点单次消费 | 流式多处理 |
冷热类型 | 热数据通道 | 默认为冷流 |
使用场景 | 生产者-消费者 | 数据处理管道 |
背压处理 | 需手动控制 | 内置操作符 |
问题3:StateFlow和LiveData如何选择?
-
选择StateFlow:
-
纯Kotlin项目
-
需要复杂数据转换
-
需要协程集成
-
-
选择LiveData:
-
现有Java代码库
-
简单UI状态管理
-
需要生命周期感知
-
问题4:如何在协程中处理异常?
最佳实践:
// 方式1:try-catch
launch {try {riskyOperation()} catch (e: Exception) {handleError(e)}
}// 方式2:CoroutineExceptionHandler
val handler = CoroutineExceptionHandler { _, e ->println("捕获异常: $e")
}CoroutineScope(Dispatchers.IO + handler).launch {throw RuntimeException("测试异常")
}
四、协程通信模式选择指南
场景 | 推荐方案 | 代码示例 |
---|---|---|
UI状态管理 | StateFlow | MutableStateFlow(initial) |
全局事件通知 | SharedFlow | MutableSharedFlow(replay=1) |
一次性数据传递 | Channel | Channel<T>() |
复杂数据处理 | Flow | flow { emit(data) } |
多源优先响应 | Select | select { chan1.onReceive } |
父子协程通信 | 直接访问变量 | parentVar = value |