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参数所设置的,总共有四种启动模式,如下所示:

  1. DEFAULT:创建协程之后,立即开始调度,在调度前如果协程被取消,其将直接进入取消响应状态
  2. ATOMIC:协程创建后,立即开始调度,协程执行到第一个挂起点之前不响应取消
  3. LAZY:只有协程被需要时,包括主动调用start,join,await等函数时才会开始调度,如果调度前被取消协程就会进入异常结束状态
  4. UNDISPATCHED:协程创建之后立即在当前函数的调用栈中执行,直到遇到第一个真正挂起的点

  这里我们要搞清楚立即调度和立即执行的区别,立即调度表示协程的调度器会立即接收调度指令,但具体执行的时机以及在哪个线程上执行还需要根据调度器的情况而定,也就是说立即调度到立即执行前通常会隔一段时间。这里,我们给出一段代码,每隔一段时间打印一个数字:

lifecycleScope.launch{
for(a in 1..10){
Log.i("lifecycleScope",a.toString())
delay(1000)
}
}

  这里需要注意的是,如果不指定调度器,那么该协程默认运行在UI线程上,指定调度器可以通过context参数指定,和上一节我们实现的一样,这里不再赘述。

  lauch函数的返回值是Job对象,Job对象常用的属性和函数如下:

  1. isActive:判断Job是否处于活动状态
  2. isCompleted:判断Job是否属于完成状态
  3. isCancelled:判断Job是否被取消
  4. start():开始Job
  5. cancel():取消Job
  6. join():将当前协程挂起,直到该协程完成
  7. 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协程系列(三)的更多相关文章

  1. Kotlin协程解析系列(上):协程调度与挂起

    vivo 互联网客户端团队- Ruan Wen 本文是Kotlin协程解析系列文章的开篇,主要介绍Kotlin协程的创建.协程调度与协程挂起相关的内容 一.协程引入 Kotlin 中引入 Corout ...

  2. 深入理解协程(三):async/await实现异步协程

    原创不易,转载请联系作者 深入理解协程分为三部分进行讲解: 协程的引入 yield from实现异步协程 async/await实现异步协程 本篇为深入理解协程系列文章的最后一篇. 从本篇你将了解到: ...

  3. Kotlin 协程一 —— 全面了解 Kotlin 协程

    一.协程的一些前置知识 1.1 进程和线程 1.1.1基本定义 1.1.2为什么要有线程 1.1.3 进程与线程的区别 1.2 协作式与抢占式 1.2.1 协作式 1.2.2 抢占式 1.3 协程 二 ...

  4. {python之协程}一 引子 二 协程介绍 三 Greenlet 四 Gevent介绍 五 Gevent之同步与异步 六 Gevent之应用举例一 七 Gevent之应用举例二

    python之协程 阅读目录 一 引子 二 协程介绍 三 Greenlet 四 Gevent介绍 五 Gevent之同步与异步 六 Gevent之应用举例一 七 Gevent之应用举例二 一 引子 本 ...

  5. Kotlin协程第一个示例剖析及Kotlin线程使用技巧

    Kotlin协程第一个示例剖析: 上一次https://www.cnblogs.com/webor2006/p/11712521.html已经对Kotlin中的协程有了理论化的了解了,这次则用代码来直 ...

  6. Retrofit使用Kotlin协程发送请求

    Retrofit2.6开始增加了对Kotlin协程的支持,可以通过suspend函数进行异步调用.本文简单介绍一下Retrofit中协程的使用 导入依赖 app的build文件中加入: impleme ...

  7. Kotlin协程基础

    开发环境 IntelliJ IDEA 2021.2.2 (Community Edition) Kotlin: 212-1.5.10-release-IJ5284.40 我们已经通过第一个例子学会了启 ...

  8. Android Kotlin协程入门

    Android官方推荐使用协程来处理异步问题.以下是协程的特点: 轻量:单个线程上可运行多个协程.协程支持挂起,不会使正在运行协程的线程阻塞.挂起比阻塞节省内存,且支持多个并行操作. 内存泄漏更少:使 ...

  9. rxjava回调地狱-kotlin协程来帮忙

    本文探讨的是在tomcat服务端接口编程中, 异步servlet场景下( 参考我另外一个文章),用rxjava来改造接口为全流程异步方式 好处不用说 tomcat的worker线程利用率大幅提高,接口 ...

  10. Kotlin协程通信机制: Channel

    Coroutines Channels Java中的多线程通信, 总会涉及到共享状态(shared mutable state)的读写, 有同步, 死锁等问题要处理. 协程中的Channel用于协程间 ...

随机推荐

  1. 【译】摇摆你的调试游戏:你需要知道的 Parallel Stack Window 小知识!

    在 Visual Studio 2022 17.6和17.7中,我们在 Parallel Stack 窗口中添加了大量新功能,可以将您的多线程调试提升到一个新的水平. 但是 Parallel Stac ...

  2. .NET5从零基础到精通:全面掌握.NET5开发技能

    C#版本新语法-官网: C#7:https://docs.microsoft.com/zh-cn/dotnet/csharp/whats-new/csharp-7 C#8:https://docs.m ...

  3. 通过python,将excel中的数据写入二维列表

    需求:读取Excel表中数据,每行数据放在一个列表中,再把所有列表都存入到一个列表中,形成二维列表. 实现方法:导入可在Python处理Excel表格数据的模块. excel表: 方法一:xlwing ...

  4. SAP 传输请求释放及传输过程 SE10 STMS

    T-CODE:SE10 STMS 1.传输请求释放 首先通过SE10打开传输组织器. 点击[显示],可以看到待释放的请求. 此时将可修改请求中的请求,点击进行展开,可以看到子请求号和请求属性. 选中请 ...

  5. SRC赏金猎人—笔记二

    以下是如何将速率限制漏洞的影响从低增加到高甚至严重 过程 1.我访问了该网站,然后开始在网站的主文件中手动查找main.js 2.我发现有一个 Web 服务托管在 http:// redacted.c ...

  6. 深度学习(十二)——神经网络:搭建小实战和Sequential的使用

    一.torch.nn.Sequential代码栗子 官方文档:Sequential - PyTorch 2.0 documentation # Using Sequential to create a ...

  7. Flutter系列文章-Flutter 插件开发

    在本篇文章中,我们将学习如何开发 Flutter 插件,实现 Flutter 与原生平台的交互.我们将详细介绍插件的开发过程,包括如何创建插件项目.实现方法通信.处理异步任务等.最后,我们还将演示如何 ...

  8. vue 脚手架文件结构及加载过程浅谈

    1. 初始化脚手架 1.1  全局安装 @vue/cli  npm install -g @vue/cli 1.2 切换到创建项目的目录,执行 vue create projectname 1.3 选 ...

  9. 用 ChatGPT 做一个 Chrome 扩展 | 京东云技术团队

    用ChatGPT做了个Chrome Extension 最近科技圈儿最火的话题莫过于ChatGPT了. 最近又发布了GPT-4,发布会上的Demo着实吸睛. 笔记本上手画个网页原型,直接生成网页.网友 ...

  10. B2C在线教育商城--前后端分离部署

    博客地址:https://www.cnblogs.com/zylyehuo/ 技术栈:vue + nginx + uwsgi + django + mariadb + redis 基本流程 vue打包 ...