01. 协程基础
协程是 Kotlin 的轻量级并发方案。相比于线程,一个线程上可以跑成千上万个协程,因为协程挂起时不阻塞线程,线程可以去干别的事。suspend 标记可挂起函数,launch 启动不关心返回值的协程,async 启动并返回 Deferred(类似 Future)。协程上下文决定了协程跑在哪个线程上。
kotlin
import kotlinx.coroutines.*
fun main() = runBlocking {
// launch 启动协程
val job = launch {
delay(1000) // 挂起,不阻塞线程
println("协程执行完毕")
}
// async 带返回值
val deferred = async {
delay(500)
"结果"
}
println("等待结果...")
val result = deferred.await()
println("获得: $result")
job.join() // 等待 launch 协程结束
}delay 是协程的 sleep,挂起不阻塞线程;Thread.sleep 是真正阻塞线程。
02. 协程上下文和调度器
协程调度器决定协程在哪个线程上跑。Dispatchers.Main 是 Android 主线程(更新 UI),Dispatchers.IO 处理网络和数据库,Dispatchers.Default 做 CPU 密集型计算,Dispatchers.Unconfined 不限定线程。withContext 可以临时切换上下文,不会创建新协程。
kotlin
fun main() = runBlocking {
// 在 IO 线程做网络请求
val data = withContext(Dispatchers.IO) {
fetchFromNetwork()
}
// 回到主线程更新 UI
withContext(Dispatchers.Main) {
showData(data)
}
// 组合使用
launch(Dispatchers.Default) {
// CPU 密集计算
val result = heavyComputation()
withContext(Dispatchers.Main) {
updateUI(result)
}
}
}
suspend fun fetchFromNetwork(): String {
delay(500)
return "网络数据"
}
fun showData(data: String) = println(data)
fun heavyComputation(): String = "计算完成"
fun updateUI(result: String) = println("UI: $result")withContext 挂起当前协程,在指定调度器执行代码块,完成后回来继续,非常优雅。
03. 协程作用域和生命周期
协程作用域控制协程的生命周期。GlobalScope 是全局作用域(不推荐,生命周期不可控)。lifecycleScope 绑定 Android 的 Activity/Fragment 生命周期。viewModelScope 绑定 ViewModel 的生命周期。结构化并发:父协程会等所有子协程结束,子协程出错父协程也会取消。
kotlin
// Activity 中使用 lifecycleScope
class MyActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
lifecycleScope.launch {
// Activity 销毁时自动取消
val data = loadData()
updateUI(data)
}
}
}
// ViewModel 中使用 viewModelScope
class MyViewModel : ViewModel() {
fun loadData() {
viewModelScope.launch {
// ViewModel 清除时自动取消
val users = fetchUsers()
_users.value = users
}
}
}
// 自定义 CoroutineScope
class DataLoader {
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
fun load() {
scope.launch {
// 手动管理生命周期
}
}
fun destroy() {
scope.cancel() // 取消所有协程
}
}不要用 GlobalScope,它的生命周期和 Application 一样长,容易造成资源泄漏。
04. 异常处理和取消
协程的异常分两种:launch 的异常会直接抛给父协程导致整个作用域取消,async 的异常封装在 await 时才抛出。SupervisorJob 防止一个子协程失败影响其他子协程。用 try-catch 包裹协程内部代码处理异常,CoroutineExceptionHandler 做全局异常兜底。
kotlin
fun main() = runBlocking {
val handler = CoroutineExceptionHandler { _, exception ->
println("捕获异常: ${exception.message}")
}
// SupervisorJob 隔离错误
val scope = CoroutineScope(SupervisorJob() + handler)
scope.launch {
delay(100)
throw RuntimeException("子协程1崩了")
}
scope.launch {
delay(200)
println("子协程2不受影响,正常执行")
}
delay(500)
// 取消协程
val job = launch {
try {
repeat(1000) { i ->
println("工作 $i")
delay(100)
}
} finally {
println("清理资源")
}
}
delay(300)
job.cancel() // 取消
println("已取消")
}协程取消是协作式的:只有挂起点才会检查取消,纯计算不挂起的循环要手动检查 isActive。
05. Flow 冷数据流
Flow 是协程版的响应式流,类似 RxJava 但更轻量。flow {} 构建器创建数据流,emit 发射数据。collect 收集数据时才执行(冷流)。操作符 map/filter/take 等中间处理,collect 才是终端操作。flowOn 改变执行上游的协程上下文。
kotlin
import kotlinx.coroutines.flow.*
fun getNumbers(): Flow<Int> = flow {
for (i in 1..5) {
delay(100) // 模拟网络延迟
emit(i)
}
}
fun main() = runBlocking {
getNumbers()
.filter { it % 2 == 0 }
.map { it * 10 }
.collect { value ->
println("收到: $value")
}
// 输出: 收到: 20, 收到: 40
// StateFlow - 热流(有状态)
val stateFlow = MutableStateFlow(0)
stateFlow.value = 42
// SharedFlow - 热流(事件)
val events = MutableSharedFlow<String>()
launch {
events.collect { event -> println("事件: $event") }
}
events.emit("登录成功")
}Flow 是冷流(被收集时才执行),StateFlow/SharedFlow 是热流(始终存在)。UI 状态用 StateFlow,一次性事件用 SharedFlow。
知识测验
第 1/4 题正确 0
协程的挂起函数 suspend 和普通函数的区别?
下一节
下一节 Kotlin + Spring Boot