Kotlin协程系列(三)
1.前言
前面两节,我们运用了kotlin提供的简单协程去实现了一套更易用的复合协程,这些基本上是以官方协程框架为范本进行设计和实现的。虽然我们还没有直接接触kotlin官方协程框架,但对它的绝大多数功能已经了如指掌了。本节,我们来探讨一下官方协程框架的更多功能,并将其运用到实际的生产当中,在这里,我以在Android中使用kotlin官方协程框架为例进行讲述。
2.launch函数启动一个协程
在Android开发中,我们一般将协程的作用域和Android组件的lifeCycle绑定在一起,这样,当组件销毁的时候,协程的作用域就会取消,协程也就销毁了,这样不会造成内存泄漏。在ViewModel中,我们可以直接使用viewModelScope这个作用域去创建协程,在Activity/Fragment这些拥有生命周期的组件中,我们可以使用lifecycleScope去创建协程,这里我们使用lifecycleScope进行讲述。
这里我们先给出launch函数的官方实现:
public fun CoroutineScope.launch(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit
): Job {
val newContext = newCoroutineContext(context)
val coroutine = if (start.isLazy)
LazyStandaloneCoroutine(newContext, block) else
StandaloneCoroutine(newContext, active = true)
coroutine.start(start, coroutine, block)
return coroutine
}
我们先补充一个知识点,协程的启动模式,也就是start参数所设置的,总共有四种启动模式,如下所示:
- DEFAULT:创建协程之后,立即开始调度,在调度前如果协程被取消,其将直接进入取消响应状态
- ATOMIC:协程创建后,立即开始调度,协程执行到第一个挂起点之前不响应取消
- LAZY:只有协程被需要时,包括主动调用start,join,await等函数时才会开始调度,如果调度前被取消协程就会进入异常结束状态
- UNDISPATCHED:协程创建之后立即在当前函数的调用栈中执行,直到遇到第一个真正挂起的点
这里我们要搞清楚立即调度和立即执行的区别,立即调度表示协程的调度器会立即接收调度指令,但具体执行的时机以及在哪个线程上执行还需要根据调度器的情况而定,也就是说立即调度到立即执行前通常会隔一段时间。这里,我们给出一段代码,每隔一段时间打印一个数字:
lifecycleScope.launch{
for(a in 1..10){
Log.i("lifecycleScope",a.toString())
delay(1000)
}
}
这里需要注意的是,如果不指定调度器,那么该协程默认运行在UI线程上,指定调度器可以通过context参数指定,和上一节我们实现的一样,这里不再赘述。
lauch函数的返回值是Job对象,Job对象常用的属性和函数如下:
- isActive:判断Job是否处于活动状态
- isCompleted:判断Job是否属于完成状态
- isCancelled:判断Job是否被取消
- start():开始Job
- cancel():取消Job
- join():将当前协程挂起,直到该协程完成
- cancelAndJoin():前两个函数的结合体
3.async函数启动一个协程
async和launch函数的不同点在于launch函数启动的协程是没有返回值的,而async函数启动的协程是有返回值的。async函数返回一个Deferred对象,它继承自Job对象,我们可以通过Deferred对象中的await函数获取协程的执行结果,代码如下:
lifecycleScope.launch{
val deferred=async{
"计算结果"
}
val result=deferred.await()
Log.i("lifecycleScope",result)
}
async函数和launch函数的共同点是他们不会等待协程执行结束,会立马往下执行,测试如下:
lifecycleScope.launch {
lastTime = System.currentTimeMillis()
async {
delay(1000)
}
async {
delay(1000)
}
Log.i("耗时", (System.currentTimeMillis() - lastTime).toString())
}
打印的结果是1ms,并不是2000毫秒,也就是说多个async函数是并行执行的,当然,这里换成launch结果也是一样的。当然,如果你在后面加一个await函数,那么结果就是2000ms左右了,也就是这样:
lifecycleScope.launch {
lastTime = System.currentTimeMillis()
async {
delay(1000)
}.await()
async {
delay(1000)
}.await()
Log.i("耗时", (System.currentTimeMillis() - lastTime).toString())
}
使用launch加上join函数的结果也是一样的。如果我再换一种写法:
lifecycleScope.launch {
lastTime = System.currentTimeMillis()
val job1=launch {
delay(1000)
}
val job2=launch {
delay(1000)
}
job1.join()
job2.join()
Log.i("耗时", (System.currentTimeMillis() - lastTime).toString())
}
这里的执行结果是1000毫秒左右,可以自行尝试,换成async函数加await也是一样的。
通过上面的测试,我们可以得出结论,launch函数和async函数启动的协程是并行执行的,并且启动协程之后会立马往下执行,不会等待协程完成,除非调用join或await函数。launch函数和async函数的唯一区别就是async函数启动的协程有返回值,如果不需要获取协程的执行结果,那么没必要用async函数。
4.withContext函数的作用
官方框架中还为我们提供了一个好用的api,withContext(),它的定义如下:
public suspend fun <T> withContext(
context: CoroutineContext,
block: suspend CoroutineScope.() -> T
): T
withContext会将参数中的lambda表达式调度到由context指定的调度器上运行,并且它会返回协程体当中的返回值,它的作用几乎和async{}.await()等价,但和async{}.await()相比,它的内存开销更低,因此对于使用async后立即要调用await的情况,应当优先使用withContext函数。而且有了withContext之后,在Android开发的时候,就可以不再使用Handler了,我们可以在需要进行耗时操作(网络请求,数据库读写,文件读写)时,使用withContext切换到IO线程上,在得到想要的结果后要更新UI时又可以切换到UI线程上,非常的方便。测试如下:
lifecycleScope.launch(Dispatchers.IO) {
delay(1000)
val result="JJLin"
withContext(Dispatchers.Main){
tv_display.text=result
}
}
这段代码模拟了在IO线程上进行耗时操作,可以是数据库访问,网络请求之类的;拿到结果后,用withContext切换到主线程,进行UI的更新。
5.协程的超时取消
kotlin官方协程框架为我们提供了一个withTimeout()函数用于执行超时取消设置,这个api的定义如下:
public suspend fun <T> withTimeout(timeMillis: Long, block: suspend CoroutineScope.() -> T): T
这个函数可以设置一个超时时间,超过这个时间后就会通过抛出异常来取消这个协程,如果不想抛出异常,可以使用withTimeoutOrNull,这个函数在超时之后会返回null,而不会抛出异常。
Kotlin协程系列(三)的更多相关文章
- Kotlin协程解析系列(上):协程调度与挂起
vivo 互联网客户端团队- Ruan Wen 本文是Kotlin协程解析系列文章的开篇,主要介绍Kotlin协程的创建.协程调度与协程挂起相关的内容 一.协程引入 Kotlin 中引入 Corout ...
- 深入理解协程(三):async/await实现异步协程
原创不易,转载请联系作者 深入理解协程分为三部分进行讲解: 协程的引入 yield from实现异步协程 async/await实现异步协程 本篇为深入理解协程系列文章的最后一篇. 从本篇你将了解到: ...
- Kotlin 协程一 —— 全面了解 Kotlin 协程
一.协程的一些前置知识 1.1 进程和线程 1.1.1基本定义 1.1.2为什么要有线程 1.1.3 进程与线程的区别 1.2 协作式与抢占式 1.2.1 协作式 1.2.2 抢占式 1.3 协程 二 ...
- {python之协程}一 引子 二 协程介绍 三 Greenlet 四 Gevent介绍 五 Gevent之同步与异步 六 Gevent之应用举例一 七 Gevent之应用举例二
python之协程 阅读目录 一 引子 二 协程介绍 三 Greenlet 四 Gevent介绍 五 Gevent之同步与异步 六 Gevent之应用举例一 七 Gevent之应用举例二 一 引子 本 ...
- Kotlin协程第一个示例剖析及Kotlin线程使用技巧
Kotlin协程第一个示例剖析: 上一次https://www.cnblogs.com/webor2006/p/11712521.html已经对Kotlin中的协程有了理论化的了解了,这次则用代码来直 ...
- Retrofit使用Kotlin协程发送请求
Retrofit2.6开始增加了对Kotlin协程的支持,可以通过suspend函数进行异步调用.本文简单介绍一下Retrofit中协程的使用 导入依赖 app的build文件中加入: impleme ...
- Kotlin协程基础
开发环境 IntelliJ IDEA 2021.2.2 (Community Edition) Kotlin: 212-1.5.10-release-IJ5284.40 我们已经通过第一个例子学会了启 ...
- Android Kotlin协程入门
Android官方推荐使用协程来处理异步问题.以下是协程的特点: 轻量:单个线程上可运行多个协程.协程支持挂起,不会使正在运行协程的线程阻塞.挂起比阻塞节省内存,且支持多个并行操作. 内存泄漏更少:使 ...
- rxjava回调地狱-kotlin协程来帮忙
本文探讨的是在tomcat服务端接口编程中, 异步servlet场景下( 参考我另外一个文章),用rxjava来改造接口为全流程异步方式 好处不用说 tomcat的worker线程利用率大幅提高,接口 ...
- Kotlin协程通信机制: Channel
Coroutines Channels Java中的多线程通信, 总会涉及到共享状态(shared mutable state)的读写, 有同步, 死锁等问题要处理. 协程中的Channel用于协程间 ...
随机推荐
- C#/.NET/.NET Core优秀项目和框架每周精选(坑已挖,欢迎大家踊跃提交PR或者Issues中留言)
前言 注意:排名不分先后,都是十分优秀的开源项目和框架,每周定期更新分享(欢迎关注公众号:追逐时光者,第一时间获取每周精选分享资讯). 每周精选优秀的C#/.NET/.NET Core项目和框架,帮助 ...
- Wow: 基于 DDD、EventSourcing 的现代响应式 CQRS 架构微服务开发框架
领域驱动 | 事件驱动 | 测试驱动 | 声明式设计 | 响应式编程 | 命令查询职责分离 | 事件溯源 架构图 事件源 可观测性 OpenAPI (Spring WebFlux 集成) 自动注册 命 ...
- SpringBoot3集成RocketMq
标签:RocketMq5.Dashboard: 一.简介 RocketMQ因其架构简单.业务功能丰富.具备极强可扩展性等特点被广泛应用,比如金融业务.互联网.大数据.物联网等领域的业务场景: 二.环境 ...
- [ABC143E] Travel by Car
2023-02-20 题目 题目传送门 翻译 翻译 难度&重要性(1~10):4.5 题目来源 AtCoder 题目算法 最短路 解题思路 我们枚举每一对点 \((u_i,v_i)\) 间的距 ...
- java与es8实战之四:SpringBoot应用中操作es8(无安全检查)
欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 本篇概览 本篇是<java与es8实战>系 ...
- 《SQLi-Labs》03. Less 11~15
@ 目录 索引 Less-11 题解 原理 Less-12 题解 Less-13 题解 Less-14 题解 Less-15 题解 原理 sqli.开启新坑. 索引 Less-11:POST 回显注入 ...
- GaoNeng:我是如何为OpenTiny贡献新组件的?
本文共10076字,预计阅读20分钟 大家好啊,又是我GaoNeng.最近在给OpenTiny做贡献,感觉renderless这个架构还是挺有意思的,就贡献了一个color-picker组件,简单写篇 ...
- 深入了解 GPU 互联技术——NVLINK
随着人工智能和图形处理需求的不断增长,多 GPU 并行计算已成为一种趋势.对于多 GPU 系统而言,一个关键的挑战是如何实现 GPU 之间的高速数据传输和协同工作.然而,传统的 PCIe 总线由于带宽 ...
- 基于LangChain的LLM应用开发2——模型、提示和输出解析
本次会讲解LangChain的三个基本组件:模型.提示和解析器. 名词解析 模型(Models):是指作为基础的大语言模型.LangChain中通过ChatOpenAI或者AzureChatOpenA ...
- Maven 项目工程
maven打包java --jar1.安装jdk2.安装maven --安装非常简单 网上教程很多,此步骤忽略-- 3.配置jdk/maven环境变量 # java_jdk export JAVA ...