当前位置: 首页 > article >正文

android协程异步编程常用方法

在 Android 开发中,Kotlin 协程是处理异步操作的首选方案,它能让异步代码更简洁、更易读。以下是 Android 协程异步编程的常用方法和模式:

一、基础构建块

1. launch
  • 作用:启动一个新协程,不返回结果。
  • 适用场景:执行不需要返回值的异步任务(如 UI 更新、启动后台操作)。
  • 示例
    viewModelScope.launch {// 默认在主线程执行updateUI()// 切换到 IO 线程执行耗时操作withContext(Dispatchers.IO) {saveDataToDisk()}
    }
    
2. async + await
  • 作用:异步执行任务并返回 Deferred 对象,通过 await() 获取结果。
  • 适用场景:并行执行多个异步任务,合并结果。
  • 示例
    viewModelScope.launch {val task1 = async(Dispatchers.IO) { fetchDataFromNetwork() }val task2 = async(Dispatchers.IO) { loadCacheFromDisk() }// 等待两个任务完成并合并结果val result = combine(task1.await(), task2.await())updateUI(result)
    }
    
3. withContext
  • 作用:在指定调度器中执行代码块,并挂起当前协程,返回结果。
  • 适用场景:切换线程执行耗时操作后回到原线程。
  • 示例
    viewModelScope.launch {// 主线程:显示加载状态showLoading()// 切换到 IO 线程执行耗时操作val data = withContext(Dispatchers.IO) {repository.fetchData()}// 自动回到主线程:更新 UIupdateUI(data)
    }
    

二、调度器(Dispatchers)

调度器作用适用场景
Dispatchers.Main主线程,用于 UI 操作更新 TextView、启动动画
Dispatchers.IO后台线程,优化磁盘 / 网络 IO文件读写、网络请求
Dispatchers.Default后台线程,用于 CPU 密集型任务复杂计算、JSON 解析
Dispatchers.Unconfined不指定固定线程,由调用者决定测试、初期协程启动

三、取消与异常处理

1. 自动取消(ViewModelScope)
class MyViewModel : ViewModel() {fun fetchData() {// 使用 viewModelScope,Activity/Fragment 销毁时自动取消viewModelScope.launch {val data = withContext(Dispatchers.IO) {repository.loadData()}_liveData.value = data}}
}
2. 手动取消协程
private var job: Job? = nullfun startTask() {job = viewModelScope.launch {// 执行耗时操作}
}fun cancelTask() {job?.cancel() // 手动取消协程
}
3. 异常处理
viewModelScope.launch {try {val result = withContext(Dispatchers.IO) {api.fetchData() // 可能抛出异常的操作}updateUI(result)} catch (e: IOException) {showError(e.message)}
}

四、Flow(数据流)

1. 冷流(Cold Flow)
  • 特点:只有订阅时才执行,类似 RxJava 的 Observable。
  • 示例
    // 从数据库获取实时数据
    fun getUserFlow(): Flow<User> = flow {while (true) {emit(database.getUser()) // 发送数据delay(1000) // 每秒更新一次}
    }.flowOn(Dispatchers.IO) // 指定流的执行线程// 在 ViewModel 中收集
    viewModelScope.launch {userFlow.collect { user ->_liveData.value = user // 更新 LiveData}
    }
    
2. StateFlow & SharedFlow
  • StateFlow:保存最新值的热流,适合表示状态。

    // ViewModel 中
    private val _uiState = MutableStateFlow<UiState>(Loading)
    val uiState: StateFlow<UiState> = _uiState// 协程中更新状态
    _uiState.value = Success(data)
    
  • SharedFlow:更灵活的热流,可配置重放次数、缓冲区大小。

    private val _event = MutableSharedFlow<Event>()
    val event: SharedFlow<Event> = _event// 发送事件
    viewModelScope.launch {_event.emit(ToastEvent("操作成功"))
    }
    

五、Channel(通道)

1. 基本用法
  • 作用:在协程间传递数据,类似生产者 - 消费者模式。
  • 示例
    val channel = Channel<String>()// 生产者协程
    viewModelScope.launch {repeat(5) {delay(1000)channel.send("消息 $it")}channel.close() // 发送完毕后关闭
    }// 消费者协程
    viewModelScope.launch {for (msg in channel) {Log.d("Channel", "收到: $msg")}
    }
    

六、组合多个异步操作

1. 串行执行
viewModelScope.launch {val user = withContext(Dispatchers.IO) { api.getUser() }val orders = withContext(Dispatchers.IO) { api.getOrders(user.id) }updateUI(orders)
}
2. 并行执行
viewModelScope.launch {val userDeferred = async(Dispatchers.IO) { api.getUser() }val ordersDeferred = async(Dispatchers.IO) { api.getOrders(userId) }val user = userDeferred.await()val orders = ordersDeferred.await()updateUI(user, orders)
}
3. 使用 zip 合并流
val flow1 = flow { emit(1) }
val flow2 = flow { emit("A") }// 合并两个流的结果
flow1.zip(flow2) { num, str -> "$num-$str" }.collect { result -> Log.d("Zip", result) } // 输出 "1-A"

七、最佳实践

  1. 避免在主线程执行耗时操作

    // 错误:在主线程执行网络请求
    viewModelScope.launch {val data = api.fetchData() // 耗时操作,应切换到 IO 线程
    }// 正确:使用 withContext 切换线程
    viewModelScope.launch {val data = withContext(Dispatchers.IO) {api.fetchData()}
    }
    
  2. 使用 ViewModelScope 管理生命周期

    class MyViewModel : ViewModel() {fun loadData() {// 使用 viewModelScope,自动与 ViewModel 生命周期绑定viewModelScope.launch { ... }}
    }
    
  3. 避免使用 GlobalScope

    // 错误:可能导致内存泄漏
    GlobalScope.launch { ... }// 正确:使用限定作用域的协程
    viewModelScope.launch { ... }
    lifecycleScope.launch { ... }
    

八、与其他组件结合

1. 与 LiveData 结合
// 将 Flow 转换为 LiveData
val liveData: LiveData<User> = flow {emit(repository.getUser())
}.flowOn(Dispatchers.IO).asLiveData()
2. 与 Room 结合
// DAO 方法返回 Flow
@Query("SELECT * FROM users")
fun getUsers(): Flow<List<User>>// ViewModel 中收集数据
viewModelScope.launch {userDao.getUsers().collect { users ->_usersLiveData.value = users}
}

通过合理使用上述方法,你可以在 Android 中编写简洁、高效且安全的异步代码,避免回调地狱和内存泄漏问题。

相关文章:

android协程异步编程常用方法

在 Android 开发中&#xff0c;Kotlin 协程是处理异步操作的首选方案&#xff0c;它能让异步代码更简洁、更易读。以下是 Android 协程异步编程的常用方法和模式&#xff1a; 一、基础构建块 1. launch 作用&#xff1a;启动一个新协程&#xff0c;不返回结果。适用场景&…...

【计算机网络】应用层协议Http——构建Http服务服务器

&#x1f525;个人主页&#x1f525;&#xff1a;孤寂大仙V &#x1f308;收录专栏&#x1f308;&#xff1a;计算机网络 &#x1f339;往期回顾&#x1f339;&#xff1a; 【Linux笔记】——进程间关系与守护进程 &#x1f516;流水不争&#xff0c;争的是滔滔不息 一、Http协…...

【求A类B类月】2022-2-9

缘由编程求解&#xff0c;如内容所示题-Python-CSDN问答只写示例及注释 每月工作日只考虑周末情况&#xff0c;即只有周六、周日放假。每月第一个工作日如果是星期一则该月是A类月&#xff0c;每月最后一个工作日如果是星期五则该月是B类月。一个月可能是A类月也可能是B类月。…...

信息安全之为什么引入公钥密码

在对称密码中&#xff0c;由于加密和解密的密钥是相同的&#xff0c;因此必须向接收者配送密钥&#xff0c;这里就涉及到密钥配送问题 那么什么时候密钥配送问题呢&#xff1f;举个简单的例子大家就清楚了&#xff0c; Alice 前几天在网上认识了Bob&#xff0c;现在她想给Bob…...

linux版本vmware修改ubuntu虚拟机为桥接模式

1、先打开linux版本vmware操作界面 2、设置虚拟路由编辑器的桥接模式 输入账号密码 自动模式 不需要进行任何操作 3、修改虚拟机设置网络模式为桥接模式 然后save保存一下配置 4、现在进入虚拟机查看ens33配置 网卡启动但是没有ip 5、自己进行设置修改ubuntu网络配置文件 cd …...

pytest 常见问题解答 (FAQ)

pytest 常见问题解答 (FAQ) 1. 基础问题 Q1: 如何让 pytest 发现我的测试文件&#xff1f; 测试文件命名需符合 test_*.py 或 *_test.py 模式测试函数/方法需以 test_ 开头测试类需以 Test 开头(且不能有__init__方法) Q2: 如何运行特定测试&#xff1f; pytest path/to/t…...

从0到1上手Trae:开启AI编程新时代

摘要&#xff1a;字节跳动 2025 年 1 月 19 日发布的 Trae 是一款 AI 原生集成开发环境工具&#xff0c;3 月 3 日国内版推出。它具备 AI 问答、代码自动补全、基于 Agent 编程等功能&#xff0c;能自动化开发任务&#xff0c;实现端到端开发。核心功能包括智能代码生成与补全、…...

HTTPS 协议:数据传输安全的坚实堡垒

在互联网技术飞速发展的今天&#xff0c;数据在网络中的传输无处不在。从日常浏览网页、在线购物&#xff0c;到企业间的数据交互&#xff0c;每一次信息传递都关乎着用户隐私、企业利益和网络安全。HTTP 协议作为互联网应用层的基础协议&#xff0c;曾经承担着数据传输的重任&…...

Spring Boot中使用@JsonAnyGetter和@JsonAnySetter处理动态JSON属性

Spring Boot 中使用 @JsonAnyGetter 和 @JsonAnySetter 处理动态 JSON 属性 在实际的后端开发中,尤其是使用 Spring Boot 构建 API 时,我们经常会遇到需要处理动态 JSON 属性的场景。例如,前端传递过来的 JSON 数据结构不固定,或者业务需求变更频繁,导致实体类无法预先定…...

Spring Boot测试框架全面解析

Spring Boot测试框架基础 Spring Boot通过增强Spring测试框架的能力,为开发者提供了一系列简化测试流程的新注解和特性。该框架建立在成熟的Spring测试基础之上,通过自动化配置和专用注解显著提升了测试效率。 核心依赖配置 要使用Spring Boot的全部测试功能,只需在项目中…...

Linux之MySQL安装篇

1.确保Yum环境是否能正常使用 使用yum环境进行软件的安装 yum -y install mysql-server mysql2.确保软件包已正常完成安装 3.设置防火墙和selinux配置 ## 关闭防火墙 systemctl stop firewalld## 修该selinux配置 vim /etc/selinux/config 将seliuxenforcing修改为sel…...

Asp.Net Core 如何配置在Swagger中带JWT报文头

文章目录 前言一、配置方法二、使用1、运行应用程序并导航到 /swagger2、点击右上角的 Authorize 按钮。3、输入 JWT 令牌&#xff0c;格式为 Bearer your_jwt_token。4、后续请求将自动携带 Authorization 头。 三、注意事项总结 前言 配置Swagger支持JWT 一、配置方法 在 …...

第12讲、Odoo 18 权限控制机制详解

目录 引言权限机制概述权限组&#xff08;Groups&#xff09;访问控制列表&#xff08;ACL&#xff09;记录规则&#xff08;Record Rules&#xff09;字段级权限控制按钮级权限控制菜单级权限控制综合案例&#xff1a;多层级权限控制最佳实践与注意事项总结 引言 Odoo 18 提…...

8086 处理器 Flags 标志位全解析:CPU 的 “晴雨表” 与 “遥控器”总结:

引入&#xff1a; 你是否好奇&#xff0c;当 CPU 执行一条加法指令时&#xff0c;如何自动判断结果是否超出范围&#xff1f;当程序跳转时&#xff0c;如何快速决定走哪条分支&#xff1f;甚至在调试程序时&#xff0c;为何能让 CPU “一步一停”&#xff1f;这一切的答案&…...

具有离散序列建模的统一多模态大语言模型【AnyGPT】

第1章 Instruction 在人工智能领域、多模态只语言模型的发展正迎来新的篇章。传统的大型语言模型(LLM)在理解和生成人类语言方面展现出了卓越的能力&#xff0c;但这些能力通常局限于 文本处理。然而&#xff0c;现实世界是一个本质上多模态的环境&#xff0c;生物体通过视觉、…...

PHP HTTP 完全指南

PHP HTTP 完全指南 引言 PHP 作为一种流行的服务器端脚本语言,广泛应用于各种Web开发项目中。HTTP(超文本传输协议)是互联网上应用最为广泛的网络协议之一,用于在Web服务器和客户端之间传输数据。本文将详细介绍 PHP 在 HTTP 通信中的应用,帮助开发者更好地理解和利用 P…...

物流项目第九期(MongoDB的应用之作业范围)

本项目专栏&#xff1a; 物流项目_Auc23的博客-CSDN博客 建议先看这期&#xff1a; MongoDB入门之Java的使用-CSDN博客 需求分析 在项目中&#xff0c;会有两个作业范围&#xff0c;分别是机构作业范围和快递员作业范围&#xff0c;这两个作业范围的逻辑是一致的&#xf…...

系统思考:经营决策沙盘

今年是我为黄浦区某国有油漆涂料企业提供经营决策沙盘培训的第二年。在这段时间里&#xff0c;我越来越感受到&#xff0c;企业的最大成本往往不在生产环节&#xff0c;而是在决策错误上所带来的长远影响。尤其是在如今这个复杂多变的环境下&#xff0c;企业面临的挑战愈发严峻…...

[网页五子棋][对战模块]实现游戏房间页面,服务器开发(创建落子请求/响应对象)

实现游戏房间页面 创建 css/game_room.css #screen 用于显示当前的状态&#xff0c;例如“等待玩家连接中…”&#xff0c;“轮到你落子”&#xff0c;“轮到对方落子”等 #screen { width: 450px; height: 50px; margin-top: 10px; color: #8f4e19; font-size: 28px; …...

数据结构-代码总结

下面代码自己上完课写着玩的,除了克鲁斯卡尔那里完全ai&#xff0c;其他基本上都是自己写的&#xff0c;具体请参考书本,同时也欢迎各位大佬来纠错 线性表 //线性表--顺序存储结构 #include<iostream> using namespace std; template<typename T> …...

快速掌握 GO 之 RabbitMQ

更多个人笔记见&#xff1a; github个人笔记仓库 gitee 个人笔记仓库 个人学习&#xff0c;学习过程中还会不断补充&#xff5e; &#xff08;后续会更新在github和 gitee上&#xff09; 文章目录 作用经典例子生产者&#xff08;发送端&#xff09;消费者&#xff08;接收端&a…...

SQL Server 事务详解:概念、特性、隔离级别与实践

一、事务的基本概念 事务&#xff08;Transaction&#xff09;是数据库操作的基本单位&#xff0c;它是由一组SQL语句组成的逻辑工作单元。事务具有以下关键特性&#xff0c;通常被称为ACID特性&#xff1a; ​​原子性&#xff08;Atomicity&#xff09;​​&#xff1a;事务…...

MAC软件游戏打开提示已损坏

打开「终端.app」&#xff0c;输入以下命令并回车&#xff0c;输入开机密码回车 sudo spctl --master-disable 按照上述步骤操作完成后&#xff0c;打开「系统偏好设置」-「安全与隐私」-「通用」&#xff0c;确保已经修改为「任何来源」。 打开「终端.app」&#xff0c;输入…...

React基础教程(13):路由的使用

文章目录 1、什么是路由?2、路由安装3、路由使用(1)路由方法导入和使用(2)定义路由以及重定向(3)嵌套路由(4)路由跳转方式(5)动态路由动态路由写法一动态路由写法二4、实现效果5、完整代码下载1、什么是路由? 路由是根据不同的url地址展示不同的内容或页面。 一个…...

力扣刷题(第四十三天)

灵感来源 - 保持更新&#xff0c;努力学习 - python脚本学习 解题思路 1. 逐位检查法&#xff1a;通过右移操作逐位检查每一位是否为1&#xff0c;统计计数 2. 位运算优化法&#xff1a;利用 n & (n-1) 操作消除最低位的1&#xff0c;减少循环次数 3. 内置函数法&…...

Centos环境下安装/重装MySQL完整教程

目录 一、卸载残留的MySQL环境&#xff1a; 二、安装MySQL&#xff1a; 1、下载MySQL官方的yum源&#xff1a; 2、更新系统yum源&#xff1a; 3、确保系统中有了对应的MySQL安装包&#xff1a; 4、安装MySQL服务&#xff1a; 5、密钥问题安装失败解决方法&#xff1a; …...

【Linux】环境变量完全解析

9.环境变量 文章目录 9.环境变量一、命令行参数二、获取环境变量程序中获取环境变量1. 使用命令行参数2. 使用系统调用函数getenv("字符串");3. 使用系统提供的全局变量environ 命令行中查询环境变量 三、常见环境变量1. HOME2. OLDPWD3. PATH4. SHELL 四、环境变量与…...

【Java】mybatis-plus乐观锁-基本使用

乐观锁&#xff08;Optimistic Locking&#xff09;是解决并发问题的重要机制。它通过在数据更新时验证数据版本来确保数据的一致性&#xff0c;从而避免并发冲突。与悲观锁不同&#xff0c;乐观锁并不依赖数据库的锁机制&#xff0c;而是通过检查数据的版本或标志字段来判断数…...

力扣每日一题——找到离给定两个节点最近的节点

目录 题目链接&#xff1a;2359. 找到离给定两个节点最近的节点 - 力扣&#xff08;LeetCode&#xff09; 题目描述 解法一&#xff1a;双指针路径交汇法​ 基本思路 关键步骤 为什么这样可行呢我请问了&#xff1f; 举个例子 特殊情况 Java写法&#xff1a; C写法&a…...

机器学习与深度学习03-逻辑回归01

目录 上集回顾1. 逻辑回归与线性回归的区别2.逻辑回归的常见目标函数3.逻辑回归如何分类4.Sigmoid函数详解5.逻辑回归模型的参数 上集回顾 上一节文章地址&#xff1a;链接 1. 逻辑回归与线性回归的区别 应用领域 线性回归通常⽤于解决回归问题&#xff0c;其中⽬标是预测⼀…...