Android Kotlin知识汇总(三)Kotlin 协程

Kotlin的重要优势及特点之——结构化并发

Kotlin 协程让异步代码像阻塞代码一样易于使用。协程可大幅简化后台任务管理,例如网络调用、本地数据访问等任务的管理。本主题介绍如何使用 Kotlin 协程解决以下问题,从而让您能够编写出更清晰、更简洁的应用代码。

所有源文件都必须编码为 UTF-8。

来源标注:Android 上的 Kotlin 协程  |  Android Developers

书接上篇:Android Kotlin知识汇总(二)最佳实践-CSDN博客


Android 上的 Kotlin 协程

协程是一种并发设计模式,可以在 Android 平台上使用它来简化异步执行的代码。

在 Android 上,协程有助于管理长时间运行的任务。使用协程的专业开发者中有超过 50% 的人反映使用协程提高了工作效率。

       简单来说,协程就是一种轻量级的非阻塞的线程工具API,可以用同步的方式写出异步的代码,优雅地切换线程和处理回调地狱。与线程的关系,线程在进程中,协程在线程中。


协程特点

协程是我们在 Android 上进行异步编程的推荐解决方案。值得关注的特点包括:

  • 轻量:可以在单个线程上运行多个协程,因为协程支持挂起,不会使正在运行协程的线程阻塞。挂起比阻塞节省内存,且支持多个并行操作。
  • 内存泄漏更少:使用结构化并发机制在一个作用域内执行多项操作。
  • 内置取消支持:取消操作会自动在运行中的协程层次结构内传播。
  • Jetpack 集成:许多 Jetpack 库都包含提供全面协程支持的扩展。某些库还提供自己的协程作用域,可供您用于结构化并发。

主流创建协程的方式

使用协程时在代码写法上和普通的顺序代码类似。创建协程可以使用以下三种方式:

// 方法1:使用 runBlocking 顶层函数
runBlocking {}// 方法2:使用 GlobalScope 单例对象,调用 launch 开启协程
GlobalScope.launch {}// 方法3:创建 CoroutineScope 对象,调用 launch 开启协程
val coroutineScope = CoroutineScope(context)
coroutineScope.launch {}
  • 方法 1: 适用于单元测试场景,实际开发中不推荐,因为它是线程阻塞的;
  • 方法 2: 不会阻塞线程,但它的生命周期会和 APP 一致,且无法取消;
  • 方法 3: 推荐使用,可以通过 context 参数去管理和控制协程的生命周期。

使用协程确保主线程安全 

通过launch()创建一个新的协程空间,{}内的代码块被叫做一个子协程。而传给 launch()的参数则用于指定执行这段代码运行的线程。

coroutineScope.launch(Dispatchers.IO) {//参数切到IO线程执行
}
coroutineScope.launch(Dispatchers.Main) {//参数切到主线程执行
}

在 Kotlin 中,所有协程都必须在调度程序中运行,即使它们在主线程上运行也是如此。

协程可以自行挂起,而调度程序负责将其恢复。 

Kotlin 提供了三个调度程序,以用于指定应在何处运行协程:

  • Dispatchers.Main - 使用此调度程序可在 Android 主线程上运行协程。此调度程序只能用于与界面交互和执行快速工作。
  • Dispatchers.IO - 此调度程序经过了专门优化,适合在主线程之外执行磁盘或网络 I/O。
  • Dispatchers.Default - 此调度程序经过了专门优化,适合在主线程之外执行占用大量 CPU 资源的工作。用例示例包括对列表排序和解析 JSON。

使用 withContext 方法

        该方法支持自动切回原来的线程,能够消除并发代码在协作时产生的嵌套。如果需要频繁地进行线程切换,这种写法将有很大的优势,即“使用同步的方式写异步代码”。如下所示:

coroutineScope.launch(Dispatchers.Main) {// Dispatchers.Mainval image = withContext(Dispatchers.IO) {// 切换到 IO 线程getImage(imageUrl) // 切换到 IO 线程}imageView.setImageBitmap(image) // Dispatchers.Main
}

withContext(Dispatchers.IO) 创建一个在 IO 线程池中运行的代码块。放在该块内的任何代码都始终通过 IO 调度程序执行。由于 withContext 本身就是一个挂起函数,因此函数 getImage() 也是一个挂起函数。  

使用 suspend 关键字

协程在常规函数的基础上添加了两项操作,用于处理长时间运行的任务。 

  • suspend 用于暂停执行当前协程,并保存所有局部变量。
  • resume 用于让已挂起的协程从挂起处继续执行。

代码在执行到某个 suspend 函数时会从正在执行它的线程上脱离,协程会从被挂起的 suspend 函数指定的线程(如 Dispatchers.IO)中开始执行。当该 suspend方法执行完成之后,会重新切换回它原先的线程。这个「切回来」的动作,在 Kotlin 中叫做 resume。

在上述示例中,把 withContext 单独放进一个getImage()里,并使用 suspend 关键字标记才能编译通过,示例代码如下: 

suspend fun getImage(imageUrl: String) = // Dispatchers.MainwithContext(Dispatchers.IO) {    // Dispatchers.IO (main-safety block)/* network IO here */          }                                // Dispatchers.Main

如果调用 suspend 函数,只能从其他 suspend 函数进行调用时,代码如下所示:

suspend fun fetchDocs() { // Dispatchers.Mainval result = getImage("url") // Dispatchers.IO 
}
suspend fun getImage(url: String) = withContext(Dispatchers.IO) { /* ... */ }

在上面的示例中,getImage() 仍在主线程上运行,但它会在启动网络请求之前挂起协程。当网络请求完成时,getImage() 会恢复已挂起的协程fetchDocs(),而不是使用回调通知主线程。 

获取协程的返回值

启动协程的方式有两种:

  • launch 启动新协程而不将结果返回给调用方。
  • async  在另一个协程内或在挂起函数内且在执行“并行分解”时才使用,并可以使用一个名为 await 的挂起函数返回结果。

警告launch 和 async 处理异常的方式不同。由于 async 对 await 进行最终调用,因此它持有异常并将其作为 await 调用的一部分抛出。所以,使用 async 会有把异常静默吞掉的风险。

并行分解 

基于Kotlin 的结构化并发机制,您可以定义启动一个或多个协程的 coroutineScope。然后,您可以使用 await()或 awaitAll()保证这些协程在从函数返回结果之前完成。

例如,在一个coroutineScope里执行两个并行的协程,此时通过调用 await()对每个延迟引用,就可以保证这两项 async 操作在返回值之前完成,代码如下所示:

suspend fun fetchTwoDocs() =coroutineScope {val deferredOne = async { fetchDoc(1) }val deferredTwo = async { fetchDoc(2) }deferredOne.await()deferredTwo.await()}

此外,coroutineScope 会捕获协程抛出的所有异常,并将其传送回调用方。

协程的非阻塞式挂起

      「非阻塞式挂起」指的就是协程在挂起的同时切线程这件事情。使用了协程的代码看似阻塞,但由于协程内部做了很多工作(包括自动切换线程),它实际上是非阻塞的。        

        在代码执行的过程中,当线程执行到了 suspend 方法,就暂时不再执行剩余协程代码,跳出协程的代码块。如果它是一个后台线程,它会被系统回收或者再利用(继续执行别的后台任务),与 Java 线程池中的线程等同;如果它是 Android 主线程,它会继续执行界面刷新任务。


代码示例 

        使用协程模拟实现一个网络请求,等待时显示 Loading,请求成功或者出错让 Loading 消失,并将状态反馈给用户。

依赖项信息

如需在 Android 项目中使用协程,请将以下依赖项添加到应用的 build.gradle 文件中:

dependencies {implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9")
}

ViewModel 代码

@HiltViewModel
class MainViewModel @Inject constructor() : ViewModel() {enum class RequestStatus {IDLE, LOADING, SUCCESS, FAIL}val requestStatus = MutableStateFlow(RequestStatus.IDLE)/*** 模拟网络请求*/fun simulateNetworkRequest() {requestStatus.value = RequestStatus.LOADINGviewModelScope.launch {val requestResult = async { performSimulatedRequest() }.await()requestStatus.value = if (requestResult) RequestStatus.SUCCESS else RequestStatus.FAIL}}/*** 模拟耗时操作,随机数->成功或失败*/private suspend fun performSimulatedRequest() = withContext(Dispatchers.IO) {delay(500)val random = Random()return @withContext random.nextBoolean()}}

MainActivity 代码

使用 Jetpack Compose,将请求状态实时显示在界面上。代码如下所示:

@AndroidEntryPoint
class MainActivity : ComponentActivity() {//声明model属性private val mainViewModel: MainViewModel by viewModels()override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContent {ComposeTheme {Surface(modifier = Modifier.fillMaxSize(),color = MaterialTheme.colorScheme.background) {val requestStatusState =mainViewModel.requestStatus.collectAsState()val requestStatus by rememberSaveable {requestStatusState }Text(text = requestStatus.name,)}}}//请求网络mainViewModel.simulateNetworkRequest()}
}

小结

        在 Kotlin 中,协程就是基于线程来实现的一种更上层的工具 API,在设计思想上,协程是一个基于线程的上层框架。Kotlin 协程并没有脱离 Kotlin 或者 JVM 创造新的东西,只是简化了多线程的开发。

下一篇,继续介绍 Kotlin 协程在 Jetpack 实战开发过程中最有用的一些方面。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/739166.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Vue基础之Vuex状态管理

个人名片: 😊作者简介:一名大二在校生 🤡 个人主页:坠入暮云间x 🐼座右铭: 给自己一个梦想,给世界一个惊喜。 🎅**学习目标: 坚持每一次的学习打卡 文章目录 什么是Vuex&…

Jira自动化的实用工具——ScriptRunner简介及最佳实践

近日,龙智举办的DevSecOps研讨会年终专场“趋势展望与实战探讨:如何打好DevOps基础、赋能创新”在上海圆满落幕。龙智Atlassian技术与顾问咨询团队,以及清晖、JamaSoftware、CloudBees等生态伙伴的嘉宾发表了主题演讲,分享他们在D…

免费分享一套SpringBoot+Vue体育馆(预约)管理系统,帅呆了~~

大家好,我是java1234_小锋老师,看到一个不错的SpringBootVue体育馆(预约)管理系统,分享下哈。 项目视频演示 【免费】SpringBootVue体育馆(预约)管理系统 Java毕业设计_哔哩哔哩_bilibili【免费】SpringBootVue体育馆(预约)管理系统 Java毕…

【高通camera hal bug分析】高通自带相机镜像问题

首先打了两个log,一个是开启镜像的log,还有一个是没有开启镜像的log,如果我们开启镜像以后,观察开启镜像log发现 , 这段代码走的没有任何问题,因为Flip的值等于1了。 关闭镜像log如下: 如果我们不开启镜像…

灰度发布难以追踪?你可能用错了工具

灰度发布进行可观测性的主要方式是通过收集和分析用户在使用新版本产品或服务时的数据,以此来评估新版本的性能、稳定性和用户满意度。这个过程通常包括以下几个步骤: 定义观测指标:首先,需要定义一套合适的观测指标(…

Java Day7 常用API

文章目录 1、Arrays1.1 简述1.2 如何比较对象数组1.2.1 方法1.1.2.2 方式2 2、Lambda3、方法引用3.1 静态方法的引用3.2 实例方法引用3.3 特定类型方法引用3.4 构造器引用 1、Arrays 1.1 简述 操作数组的一个工具类 常见方法 int[] arr{10,20,30,50,40};//将 数组的内容 组…

彩色gif闪图怎么做?分享gif动画的方法

在现在的媒体时代,动态图片已经成为了最流行的分享信息的方式。能够传达动态的视觉效果。那种带有动态效果的闪动文字图片要怎么制作呢?很简单,通过使用在线制作闪字图片工具,上传多张文字内容相同,颜色、位置不同的jp…

【网络原理】使用Java基于TCP搭建简单客户端与服务器通信

目录 🎄API介绍🌸ServerSocket API🌸Socket API 🍀TCP中的长短连接🌳建立TCP回显客户端与服务器🌸TCP搭建服务器🌸TCP搭建客户端 ⭕总结 TCP服务器与客户端的搭建需要借助以下API 🎄…

C语言函数—库函数

函数是什么? 数学中我们常见到函数的概念。但是你了解C语言中的函数吗? 维基百科中对函数的定义:子程序 在计算机科学中,子程序(英语:Subroutine, procedure, function, routine, method, subprogram, ca…

c++初阶------类和对象(下)

作者前言 🎂 ✨✨✨✨✨✨🍧🍧🍧🍧🍧🍧🍧🎂 ​🎂 作者介绍: 🎂🎂 🎂 🎉🎉&#x1f389…

马斯克将在本周,开源类ChatGPT产品Grok

3月11日晚,马斯克在社交平台宣布,将在本周开源生成式AI产品——Grok。 Grok是马斯克旗下公司xAI在去年11月发布的,一款类ChatGPT产品,可以提供生成文本、代码、邮件、信息检索等功能。其测试性能超过GPT-3.5、LLaMA 2 70B&#x…

基于YOLOv8/YOLOv7/YOLOv6/YOLOv5的铁轨缺陷检测系统(Python+PySide6界面+训练代码)

摘要:开发铁轨缺陷检测系统对于物流行业、制造业具有重要作用。本篇博客详细介绍了如何运用深度学习构建一个铁轨缺陷检测系统,并提供了完整的实现代码。该系统基于强大的YOLOv8算法,并对比了YOLOv7、YOLOv6、YOLOv5,展示了不同模…

Linux运维:深入了解 Linux 目录结构

Linux运维:深入了解 Linux 目录结构 一、 Linux 目录结构与 Windows之间的主要区别二、Linux根目录结构三、常见目录及其作用 💖The Begin💖点点关注,收藏不迷路💖 一、 Linux 目录结构与 Windows之间的主要区别 1、根…

vivado clock ip核的使用

clock 偶数倍分频就是进行计数器计数,奇数倍分频如果不要求占空比的话也是进行计数,如果要求0.5的占空比的话,则需要进行两个计数器, 对于实现占空比为50%的N倍奇数分频,我们可以分解为两个通道: 上升沿触…

数据拯救指南:解决文件或目录损坏且无法读取的终极策略

在日常的计算机使用中,我们经常会遇到文件或目录损坏且无法读取的困扰。当这类问题发生时,无论是重要的文档、照片还是视频文件,都可能会变得无法访问,给我们的工作和生活带来极大的不便。面对这种情况,我们首先需要了…

落地灯哪个牌子好?五款品质可靠的落地灯,各具亮点

近年来,落地灯作为最适合目前用眼人群使用的照明电器,以显著的照明效果获得了广泛认可,并成为众多学生党、上班族用眼时的必备工具。其受欢迎的程度不断攀升,促使越来越多的人都选择入手落地灯。然而,我发现市场上也有…

关于JSP的打印调试,再来一篇巩固巩固

JSP实质上就是html混入了Java,或者说是HTMLJavaScriptCSSJava的混合⽂件,那么就会牵扯到各种语言之间的变量引用问题,既然基础是html,那么就先看看html怎么引用其他内容,以及其他内容间值的互引用。 1、Html获取JavaS…

数据结构之栈详解(C语言手撕)

🎉个人名片: 🐼作者简介:一名乐于分享在学习道路上收获的大二在校生 🙈个人主页🎉:GOTXX 🐼个人WeChat:ILXOXVJE 🐼本文由GOTXX原创,首发CSDN&…

HYBBS 表白墙网站PHP程序源码,支持封装成APP

PHP表白墙网站源码,适用于校园内或校区间使用,同时支持封装成APP。告别使用QQ空间的表白墙。 简单安装,只需PHP版本5.6以上即可。 通过上传程序进行安装,并设置账号密码,登录后台后切换模板,适配手机和PC…

如何在Linux本地搭建Tale网站并实现无公网ip远程访问

文章目录 前言1. Tale网站搭建1.1 检查本地环境1.2 部署Tale个人博客系统1.3 启动Tale服务1.4 访问博客地址 2. Linux安装Cpolar内网穿透3. 创建Tale博客公网地址4. 使用公网地址访问Tale 前言 今天给大家带来一款基于 Java 语言的轻量级博客开源项目——Tale,Tale…