Files
2026-01-15 11:53:37 +08:00

5.8 KiB
Raw Permalink Blame History

协程

目录


协程概念

什么是协程?

// 协程:轻量级线程
// 可以挂起和恢复
// 比线程更轻量,开销更小

// 创建协程
GlobalScope.launch {
    delay(1000) // 挂起1秒
    println("Hello from coroutine")
}

协程特点

  1. 轻量级:比线程更轻量,可以创建大量协程
  2. 挂起恢复:可以挂起和恢复执行
  3. 结构化并发:自动管理生命周期
  4. 简化异步代码:代码更简洁易读

协程与线程

协程 vs 线程

特性 协程 线程
创建开销
数量 可以创建大量 受系统限制
切换开销
阻塞 挂起(不阻塞线程) 阻塞线程
适用场景 异步操作 CPU 密集型任务

协程优势

// 协程:挂起不阻塞线程
suspend fun loadData() {
    delay(1000) // 挂起,不阻塞线程
    // 其他协程可以继续执行
}

// 线程:阻塞线程
Thread.sleep(1000) // 阻塞线程

协程作用域

作用域类型

1. GlobalScope

// GlobalScope全局作用域
// 生命周期与应用一致
GlobalScope.launch {
    delay(1000)
    println("Hello")
}

2. CoroutineScope

// CoroutineScope自定义作用域
val scope = CoroutineScope(Dispatchers.Main)
scope.launch {
    delay(1000)
    println("Hello")
}
scope.cancel() // 取消所有协程

3. lifecycleScope

// lifecycleScope与 LifecycleOwner 绑定
class MainActivity : AppCompatActivity() {
    fun loadData() {
        lifecycleScope.launch {
            val data = withContext(Dispatchers.IO) {
                loadDataFromNetwork()
            }
            updateUI(data)
        }
    }
}

4. viewModelScope

// viewModelScope与 ViewModel 绑定
class MyViewModel : ViewModel() {
    fun loadData() {
        viewModelScope.launch {
            val data = loadDataFromNetwork()
            // 处理数据
        }
    }
}

协程上下文

Dispatchers

// Dispatchers.Main主线程
launch(Dispatchers.Main) {
    // 更新 UI
}

// Dispatchers.IOIO 线程
launch(Dispatchers.IO) {
    // 网络请求、文件操作
}

// Dispatchers.Default默认线程池
launch(Dispatchers.Default) {
    // CPU 密集型任务
}

// Dispatchers.Unconfined不指定线程
launch(Dispatchers.Unconfined) {
    // 在调用线程执行
}

切换上下文

// 切换上下文
launch(Dispatchers.Main) {
    val data = withContext(Dispatchers.IO) {
        // 在 IO 线程执行
        loadDataFromNetwork()
    }
    // 回到主线程
    updateUI(data)
}

协程取消

取消协程

// 取消协程
val job = launch {
    delay(10000)
    println("Done")
}
job.cancel() // 取消协程

// 检查是否取消
launch {
    while (isActive) {
        // 执行任务
        delay(100)
    }
}

取消检查

// 检查取消状态
suspend fun doWork() {
    for (i in 1..1000) {
        ensureActive() // 检查是否取消
        // 执行任务
    }
}

取消异常

// 取消时抛出 CancellationException
try {
    delay(10000)
} catch (e: CancellationException) {
    // 处理取消
    throw e // 必须重新抛出
}

协程异常处理

try-catch

// 使用 try-catch
launch {
    try {
        val data = loadData()
    } catch (e: Exception) {
        // 处理异常
    }
}

CoroutineExceptionHandler

// 使用 CoroutineExceptionHandler
val handler = CoroutineExceptionHandler { _, exception ->
    Log.e("Coroutine", "Exception: $exception")
}

launch(handler) {
    throw Exception("Error")
}

SupervisorJob

// SupervisorJob子协程异常不影响其他协程
val supervisor = SupervisorJob()
val scope = CoroutineScope(Dispatchers.Main + supervisor)

scope.launch {
    // 子协程1
}

scope.launch {
    // 子协程2异常不影响子协程1
    throw Exception("Error")
}

协程最佳实践

1. 使用合适的作用域

// ✅ 推荐:使用 lifecycleScope 或 viewModelScope
lifecycleScope.launch {
    // 协程代码
}

// ❌ 不推荐:使用 GlobalScope
GlobalScope.launch {
    // 可能导致内存泄漏
}

2. 及时取消协程

// 在 onDestroy 中取消
override fun onDestroy() {
    super.onDestroy()
    job.cancel()
}

3. 使用 suspend 函数

// ✅ 推荐:使用 suspend 函数
suspend fun loadData(): String {
    return withContext(Dispatchers.IO) {
        // 网络请求
    }
}

// ❌ 不推荐:在协程中直接使用回调

面试常见问题

Q1: 什么是协程?

答案:

  • 轻量级线程
  • 可以挂起和恢复
  • 比线程更轻量,开销更小
  • 简化异步代码

Q2: 协程和线程的区别?

答案:

  • 协程:轻量级,可以创建大量,挂起不阻塞线程
  • 线程:重量级,受系统限制,阻塞线程

Q3: 协程作用域?

答案:

  1. GlobalScope:全局作用域
  2. CoroutineScope:自定义作用域
  3. lifecycleScope:与 LifecycleOwner 绑定
  4. viewModelScope:与 ViewModel 绑定

Q4: 如何取消协程?

答案:

  1. 调用 job.cancel()
  2. 检查 isActive
  3. 使用 ensureActive()

Q5: 协程异常处理?

答案:

  1. 使用 try-catch
  2. 使用 CoroutineExceptionHandler
  3. 使用 SupervisorJob

最后更新2024年