Kotlin 协程的五大常见错误用法及最佳实践

1,632 阅读2分钟

1. 不必要的线程切换

问题:在 Retrofit 的 suspend 函数外额外包裹 withContext(Dispatchers.IO)

// ❌ 冗余的线程切换
suspend fun fetchData(): Response<Data> {
    return withContext(Dispatchers.IO) {
        retrofitService.getData()
    }
}

// ✅ 简洁正确的写法
suspend fun fetchData(): Response<Data> {
    return retrofitService.getData()
}

最佳实践:Retrofit 的 suspend 函数已自动在 IO 线程执行,无需额外切换。

2. 错误的并行处理方式

问题:使用串行的 launch 而非并行的 async 执行独立任务

// ❌ 串行执行网络请求
    lifecycleScope.launch {
        val data1 = fetchDataFromNetwork1() // 等待完成
        val data2 = fetchDataFromNetwork2() // 才开始
       }

// ✅ 并行执行网络请求
    lifecycleScope.launch {
        val deferredData1 = async { fetchDataFromNetwork1() }
        val deferredData2 = async { fetchDataFromNetwork2() }
        val (data1, data2) = awaitAll(deferredData1, deferredData2)
       }

最佳实践:独立任务应使用 async 并行执行,最后统一等待结果。

3. 过早等待异步结果

问题:立即 await() 导致失去并行优势

// ❌ 伪并行
suspend fun fetchData() {
    val data1 = async { fetchDataFromNetwork1() }.await()
    val data2 = async { fetchDataFromNetwork2() }.await()
}

// ✅ 真正的并行
suspend fun fetchData() {
    val deferredData1 = async { fetchDataFromNetwork1() }
    val deferredData2 = async { fetchDataFromNetwork2() }
    val (data1, data2) = awaitAll(deferredData1, deferredData2)
}

最佳实践:先启动所有异步任务,最后统一等待,最大化并行效率。

4. 未正确使用IO调度器

问题:在主线程执行文件操作等耗时任务

// ❌ 危险的主线程IO操作
suspend fun readFile(): String {
    return File("path/to/file").readText()
}

// ✅ 正确的IO线程处理
suspend fun readFile(): String {
    return withContext(Dispatchers.IO) {
        File("path/to/file").readText()
    }
}

最佳实践:文件操作等阻塞任务必须明确指定 Dispatchers.IO

5. 混用阻塞与非阻塞操作

问题:在协程中使用 Thread.sleep() 等阻塞调用

// ❌ 危险的阻塞操作
fun fetchData() {
    lifecycleScope.launch {
        val data = fetchDataFromNetwork()
        Thread.sleep(1000) // 阻塞整个线程
    }
}

// ✅ 正确的挂起方式
fun fetchData() {
    lifecycleScope.launch {
        val data = fetchDataFromNetwork()
        delay(1000) // 非阻塞挂起
    }
}

最佳实践:始终使用 delay() 等挂起函数替代阻塞操作。

总结对比表

错误类型错误示例正确写法关键改进
冗余线程切换withContext(IO){retrofitCall}直接调用retrofit避免不必要的上下文切换
串行并行混淆顺序launch调用使用async并行最大化任务并行度
过早等待立即await()最后统一awaitAll保持并行优势
线程选择不当主线程IO操作withContext(IO)正确使用调度器
阻塞操作Thread.sleep()delay()保持协程非阻塞特性

掌握这些最佳实践,您将能够:

  1. 避免常见的协程使用误区
  2. 编写更高效的异步代码
  3. 充分利用协程的并发优势
  4. 保证应用响应性能

正确使用协程可以显著提升应用性能,同时保持代码的简洁性和可维护性。