10+ 个高级 Android 开发者应避免的常见 Kotlin 协程错误

1. 阻塞主线程

  • 错误:Dispatchers.Main 中使用 Thread.sleep() 或阻塞式调用。
  • 问题: 冻结 UI,导致 ANR(应用程序无响应)。
  • 修复: 在协程中,使用 delay() 代替 Thread.sleep() 来实现非阻塞式延迟。
kotlin 复制代码
// 错误  
Thread.sleep(1000)  
  
// 正确  
delay(1000)

2. 不加区分地使用 GlobalScope

  • 错误:GlobalScope 中启动协程,而没有生命周期感知。
  • 问题: 这会导致内存泄漏和不必要的资源消耗。
  • 修复: 使用 lifecycleScopeviewModelScope 来启动作用域协程。
kotlin 复制代码
// 错误
GlobalScope.launch {
    fetchData()
}

// 正确
viewModelScope.launch {
    fetchData()
}

3. 未正确处理异常

  • 错误: 忽略协程异常或未正确使用 try-catch
  • 问题: 导致协程崩溃和未处理的异常。
  • 修复: 使用 CoroutineExceptionHandlertry-catch 进行结构化错误处理。
kotlin 复制代码
val handler = CoroutineExceptionHandler { _, exception ->
    Log.e("Coroutine", "Caught exception: $exception")
}

CoroutineScope(Dispatchers.IO + handler).launch {
    try {
        riskyOperation()
    } catch (e: Exception) {
        Log.e("Coroutine", "Handled exception: $e")
    }
}

4. 忽略结构化并发

  • 错误: 启动多个协程而不管理它们的生命周期。
  • 问题: 协程无限期运行,导致内存泄漏或意外行为。
  • 修复: 使用 coroutineScopesupervisorScope 进行结构化并发。
kotlin 复制代码
// 错误
launch { fetchData1() }
launch { fetchData2() }

// 正确
coroutineScope {
    launch { fetchData1() }
    launch { fetchData2() }
}

5. 滥用 Dispatchers.IODispatchers.Default

  • 错误:Dispatchers.IO 上执行 CPU 密集型任务,反之亦然。
  • 问题: 导致性能不佳和线程饥饿。
  • 修复:Dispatchers.Default 用于计算任务,将 Dispatchers.IO 用于 I/O 操作。
kotlin 复制代码
// CPU密集任务
withContext(Dispatchers.Default) {
    processLargeData()
}

// I/O 操作
withContext(Dispatchers.IO) {
    fetchFromNetwork()
}

6. 过度使用 withContext

  • 错误: 不必要地嵌套多个 withContext 代码块。
  • 问题: 导致线程切换开销并降低可读性。
  • 修复: 将相关操作归入单个上下文。
scss 复制代码
// 错误
withContext(Dispatchers.IO) { operation1() }
withContext(Dispatchers.IO) { operation2() }

// 正确
withContext(Dispatchers.IO) {
    operation1()
    operation2()
}

7. 忘记取消协程

  • 错误: 当不再需要时,不取消与生命周期绑定的协程。
  • 问题: 浪费资源,如果协程与已销毁的视图交互,可能导致崩溃。
  • 修复: 明确取消协程或使用生命周期感知的作用域。
kotlin 复制代码
override fun onDestroy() {
    super.onDestroy()
    coroutineScope.cancel() // 明确取消
}

8. 在自定义作用域中忽略协程上下文

  • 错误: 创建自定义 CoroutineScope 时未指定正确的上下文。
  • 问题: 导致意外行为或协程泄漏。
  • 修复: 创建自定义作用域时,始终提供一个上下文。
kotlin 复制代码
val customScope = CoroutineScope(Dispatchers.Main + Job())

customScope.launch {
    // 协程代码
}

9. 错误使用 async 启动任务

  • 错误: 使用 async 而不加 await(),或错误地将 launch 用于需要结果的任务。
  • 问题: 这可能导致结果丢失或不必要的后台任务。
  • 修复: 当你需要结果时使用 async,当执行"即发即弃"(fire-and-forget)操作时使用 launch
kotlin 复制代码
// 正确用法
val result = async { fetchData() }
Log.d("Result", result.await())

10. 未充分利用 Flow 处理数据流

  • 错误: 使用普通的协程来处理连续或响应式的数据流。
  • 问题: 管理数据流效率低下,缺乏背压(backpressure)或生命周期处理。
  • 修复: 使用 Kotlin FlowStateFlow 进行响应式数据流和状态管理。
kotlin 复制代码
val dataFlow = flow {
    emit(fetchData())
}.flowOn(Dispatchers.IO)

lifecycleScope.launch {
    dataFlow.collect { data ->
        updateUI(data)
    }
}

11. 误解 launchrunBlocking

  • 错误: 在生产代码中使用 runBlocking 或将其与 launch 混淆。
  • 问题: runBlocking 会阻塞线程,不适用于非测试环境。
  • 修复:launch 用于异步任务,runBlocking 仅用于测试(单元测试推荐使用runTest)。
scss 复制代码
// 错误 (生产环境)
runBlocking {
    performTask()
}

// 正确
launch {
    performTask()
}

通过避免这些常见的陷阱,高级 Android 开发者可以确保他们的 Kotlin 协程实现是高效、健壮且易于维护的。 欢迎关注我的公众号:OpenFlutter

相关推荐
阿巴斯甜16 小时前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker16 小时前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq952717 小时前
Andorid Google 登录接入文档
android
黄林晴19 小时前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab1 天前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿1 天前
Android MediaPlayer 笔记
android
Jony_1 天前
Android 启动优化方案
android
阿巴斯甜1 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android
张小潇1 天前
AOSP15 Input专题InputReader源码分析
android
_小马快跑_2 天前
Kotlin | 协程调度器选择:何时用CoroutineScope配置,何时用launch指定?
android