news 2026/5/29 1:51:47

Kotlin协程序使用技巧和应用场景

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Kotlin协程序使用技巧和应用场景

目录

一、使用技巧与最佳实践

1. 作用域选择(避免内存泄漏)

2. 调度器 Dispatchers 正确选择

3. 线程切换用 withContext,并发用 async

4. 异常处理

5. 超时与取消

6. Repository 层设计原则

二、常见应用场景

三、常见坑提醒


一、使用技巧与最佳实践

1. 作用域选择(避免内存泄漏)

永远使用生命周期感知的作用域,禁止在业务代码中使用GlobalScope(其生命周期与进程一致,不会自动取消)。

作用域

绑定对象

取消时机

适用场景

viewModelScope

ViewModel

onCleared()

ViewModel 中发起网络/数据库请求

lifecycleScope

Activity/Fragment

onDestroy()

UI 相关异步(倒计时、动画、Toast)

rememberCoroutineScope()

Compose 组合

离开 Composition

Compose 点击事件内启动协程

Kotlin 协程(Coroutines)是 Android 异步编程的首选方案

2. 调度器 Dispatchers 正确选择

  • Dispatchers.Main​ — UI 线程,更新 View、LiveData/StateFlow

  • Dispatchers.IO​ — 网络请求(Retrofit)、Room 数据库、文件读写

  • Dispatchers.Default​ — CPU 密集型(JSON 解析、排序、加解密)

  • Dispatchers.Main.immediate​ — 已在主线程时避免多余消息队列投递

⚠️ Room 和 Retrofit 的suspend函数内部已自动切到 IO,Repository 层用withContext(Dispatchers.IO)包裹即可,调用方无需再切。

3. 线程切换用withContext,并发用async

// 顺序切换(最常用) viewModelScope.launch { val data = withContext(Dispatchers.IO) { api.fetch() } // IO _state.value = data // 自动回 Main } // 并行请求(加速页面加载) viewModelScope.launch { val userDeferred = async { api.getUser() } val feedDeferred = async { api.getFeed() } show(userDeferred.await(), feedDeferred.await()) }
  • launch→ 不返回结果(fire-and-forget)
  • async→ 返回 Deferred<T>,通过 await()取结果
  • 优先用 withContext而非 async{...}.await()做单纯线程切换

4. 异常处理

// 方式一:try-catch(推荐用于业务逻辑) viewModelScope.launch { try { _state.value = withContext(Dispatchers.IO) { api.fetch() } } catch (e: IOException) { _state.value = UiState.Error(e.message) } } // 方式二:CoroutineExceptionHandler(顶层兜底) val handler = CoroutineExceptionHandler { _, e -> Log.e("TAG", e.toString()) } viewModelScope.launch(handler) { /* ... */ }

supervisorScope可让子协程异常互不传播,适合多个独立任务并行。

5. 超时与取消

viewModelScope.launch { try { withTimeout(5000) { // 5秒超时自动取消 val data = api.fetchSlow() _state.value = data } } catch (e: TimeoutCancellationException) { _state.value = UiState.Timeout } }

协程取消是协作式的,长时间计算循环中用ensureActive()isActive检查取消。

6. Repository 层设计原则

suspend函数应主线程安全——内部自己切 Dispatchers.IO,ViewModel 直接调用无需关心线程:

suspend fun getNews(): List<News> = withContext(Dispatchers.IO) { api.fetchNews().also { db.newsDao().insert(it) } }

二、常见应用场景

场景

做法

网络请求 + 刷新 UI

viewModelScope.launch + withContext(IO)

Room 数据库增删改查

DAO 声明suspend,调用时withContext(IO)或直接 collect Flow

并行接口(用户信息+列表)

async { ... } + await()两个请求并发执行

倒计时/轮询/动画

lifecycleScope.launch { while(isActive) { delay(1000); tick() } }

Flow 数据观察

flow.collectAsStateWithLifecycle()在 Compose,flow.onEach{}.launchIn(viewModelScope)在传统 View

退出页面仍需完成的任务(如日志上报、文件上传)

使用 Application 级自定义CoroutineScope(SupervisorJob()+IO),不依赖 ViewModel

防重复点击

配合debounce(Flow)或在点击时用isActive/job?.isActive判断


三、常见坑提醒

  • 不要用GlobalScope.launch做 UI 相关业务

  • 不要在Dispatchers.Default做阻塞 IO(占满 CPU 线程池)

  • 不要在协程里用Thread.sleep(),改用delay()

  • 不要把suspend函数写在 UI 层直接切线程,应由数据层负责

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/29 1:48:01

微波定向耦合器:原理、指标、架构与设计实例

引言 定向耦合器是微波与毫米波系统中最基础也最重要的无源器件之一,广泛应用于功率监测、信号分配与合成、反射系数测量、收发隔离及阵列天线馈电网络等多个工程领域。在矢量网络分析仪(VNA)内部,定向耦合器承担着分离前向波与反向波的关键功能,使得通过S参数表征被测器…

作者头像 李华
网站建设 2026/5/29 1:42:13

[智能体-137]:从硬件到智能体:全层级系统记忆体系与空间开销演进

记忆是任何硬件系统、软件系统、甚至整个计算机系统最核心能力之一。从最底层的硬件装机状态&#xff0c;到CPU内部的寄存器的状态&#xff0c;再到函数内部的状态&#xff0c;线程的状态&#xff0c;进程的状态&#xff0c;再到智能体不同会话之间信息的状态&#xff0c;不同会…

作者头像 李华
网站建设 2026/5/29 1:37:17

MAX30102传感器实战:从PPG原理到心率血氧监测系统搭建

1. 项目概述与核心价值 最近在捣鼓一个健康监测的小项目&#xff0c;核心是想做一个能实时监测心率和血氧饱和度的可穿戴原型。市面上这类传感器模块不少&#xff0c;但经过一番对比和实测&#xff0c;MAX30102这颗芯片的综合表现确实让人印象深刻。它把红光LED、红外LED、光电…

作者头像 李华
网站建设 2026/5/29 1:34:23

强强联合:OpenFeign 整合 Sentinel

前言 书接前文&#xff1a; 微服务间的远程接口调用&#xff1a;OpenFeign 的使用 当项目中使用了 OpenFeign 后&#xff0c;可以很方便的进行远程服务调用&#xff0c;现在有个问题&#xff0c;假如远程服务出现故障了&#xff0c;调不了远程的接口&#xff0c;这边又着急等…

作者头像 李华