1.  什么是协程

关于协程的定义有很多,在Kotlin语言中,协程比较合理的定义应该是一个线程框架(扔物线)或者说是一种并发设计模式(官方)。它是由官方设计的一套API方便开发者进行多线程开发。

2. 协程能干什么

协程主要用途总结下来就是可以用更优雅的代码实现多线程代码。主要体现在可以用同步的方式完成原来需要异步加回调才能完成的工作。举个简单例子,从服务器获取用户信息然后显示出来,常规写法如下:

api.sendRequest(object : Callback {override fun onFail(err: Error) {}override fun onSucc(resp: String) {funOnUiThread {updateUI(resp)}}})

上面是一个比较简单的例子,进行了两次嵌套,如果网络请求有依赖,那实现起来会更复杂:

api.fetchUserInfo(object : Callback {override fun onFail(err: Error) {}override fun onSucc(resp: String) {api.fetchDetail(object : Callback {override fun onFail(err: Error) {}override fun onSucc(resp: String) {funOnUiThread {updateUI(resp)}}})}})

下面我们用协程实现这个工作:

        launch(Dispatcher.Main) {val user = withContext(Dispatchers.IO) {api.fetchUserInfo()}var detail = withContext(Dispatchers.IO) {api.fetchUserInfo(useruser)}updateUI(detail)}

可以看到代码简洁了很多,上面的代码块我们可以理解为一个协程,这个代码块在主线程执行,当要请求用户信息时,切换到了IO线程去执行,因为网络请求是个耗时操作,所以IO线程被阻塞,等待网络请求结果。此时这个协程会挂起等待请求结果,但是它并不会阻塞UI线程,我们可以理解为它从当前执行它的线程(UI线程)脱离了,释放出了当前线程。当网络请求拿到结果,代码重新被挂载到UI线程继续执行。

从上面的代码我们可以看到,通过协程我们可以用看起来同步的代码完成实际上需要在不同线程切换的工作。

3. 协程的用法

1. 简单启动一个协程

GlobalScope.launch(Dispatchers.Main) {            Log.v("Tag", "Coroutines in Thread.currentThread().name")
}

启动线程有launch和async两种方法,上面使用launch方法在主线程运行协程,系统主要提供了下面三个调度程序,Main是在主线程执行程序,通常做UI相关的工作,IO是在IO线程上运行程序,此线程做了专门优化,适用于磁盘和网络的I/O操作,Default是专门做了优化,适用于大量CPU运算的工作。

Dispatchers.Main
Dispatchers.Default
Dispatchers.IO

2. 切换线程

启动一个协程后,可以使用withContext()在不使用回调的情况下控制代码运行的线程池:

GlobalScope.launch(Dispatchers.Main) {Log.v("Tag", "xxxxxx1 in " + Thread.currentThread().name)withContext(Dispatchers.IO) {Thread.sleep(1000)Log.v("Tag", "xxxxxx2 in " + Thread.currentThread().name)}Log.v("Tag", "xxxxxx3 in " + Thread.currentThread().name)
}

上面的代码运行结果:

Tag: xxxxxx1 in main
Tag: xxxxxx2 in DefaultDispatcher-worker-1
Tag: xxxxxx3 in main

我们可以看到,代码在主线程运行,然后切到了IO线程,运行完毕后又切回主线程继续运行,这个过程并没有阻塞主线程,只是把协程挂起了。

3. suspend函数

上面切换线程的例子中,通过withContext()函数使当前协程挂起,但是如果想把切换线程后做的工作放到一个独立的函数中,需求声明此函数为suspend才可以。suspend函数只能在协程中或者另外一个suspend函数中调用,调用suspend函数时,协程会挂起,即从当前的线程脱离,等这个函数执行完毕再切回原线程继续执行。

GlobalScope.launch(Dispatchers.Main) {Log.v("Tag", "xxxxxx1 in " + Thread.currentThread().name)doIO()Log.v("Tag", "xxxxxx3 in " + Thread.currentThread().name)
}suspend fun doIO() {withContext(Dispatchers.IO) {Thread.sleep(1000)Log.v("Tag", "xxxxxx2 in " + Thread.currentThread().name)}
}

4.  协程取消与超时

如果我们在一个页面启动了一个协程,当页面关掉时我们需要取消协程,要不然等协程切回主线程更新UI时就会出错,还有一个场景就是我们在做网络请求时,通常需要设置一个超时时间。下面是协程取消与超时的例子:

GlobalScope.launch(Dispatchers.Main) {var job = GlobalScope.launch(Dispatchers.Default) {repeat(10) {Log.v("Tag", "xxxxxx1 $it ")delay(500)}}delay(2000)job.cancel()//job.join()
}

上面是一个协程取消的例子,一个协程其实是一个job,调用cancel()后取消此协程。 下面的join()函数是一个suspend函数,它的意思是等待协程工作完成。也就是说等待job协程工作完成,当前协程再继续执行。

GlobalScope.launch(Dispatchers.Main) {var user = doIO()Log.v("Tag", "xxxxxx result $user")
}suspend fun doIO(): String? {var res = withContext(Dispatchers.IO) {withTimeoutOrNull(4000) {delay(3000)"done"}}return res
}

上面是一个超时的例子,withTimeoutOrNull()函数表示,如果在指定的时间内完成了工作,就返回下面的结果("done"),如果未能完成的话会返回null。

5. async和await

前面讲过,协程在切线程后或者调用suspend函数后,虽然没有阻塞协程所在线程,但协程是被阻塞的(挂起),例如在一个协程内需要做两个不相关的请求,但是他们也是一个执行完毕再执行另外一个:

GlobalScope.launch(Dispatchers.Main) {            Log.v("Tag", "xxxx start")             var resp1 = doRequest1()var resp2 = doRequest2()Log.v("Tag", "xxxx $resp1, $resp2")
}suspend fun doRequest1(): String? {delay(2000)return "resp1"
}suspend fun doRequest2(): String? {delay(2000)return "resp2"
}

一个请求需求2秒的时间,那么这里2个请求就需要4秒的时间。这种情况很显然不合理,Kotlin可以使用async启动一个异步协程,并且可以通过一个挂起函数await()拿到返回结果,通过这种方式就可以实现多个协程并行工作:

GlobalScope.launch(Dispatchers.Main) {            Log.v("Tag", "xxxx start")             var defer1 = async {doRequest1()}var defer2 = async {doRequest2()}Log.v("Tag", "xxxx ${defer1.await()}, ${defer2.await()}")
}suspend fun doRequest1(): String? {delay(2000)return "resp1"
}suspend fun doRequest2(): String? {delay(2000)return "resp2"
}

上面通过async()启动一个协程,并且开始执行代码。async返回的是一个Deferred对象,它是Job的子类。

6. 协程扩展

GlobalScope的声明周期依赖于进程,进程结束才会退出。GlobalScope.launch的协程不能完成时需要手动cancel(),否则会造成内存泄露。为了方便使用,android提供了两个扩展  lifecycleScope  和 viewModelScope,自动绑定声明周期。在做开发时推荐使用 lifecycleScope 和 viewModelScope,使用前只需要引入下面依赖即可,lifecycleScope在Fragment和Activity中使用,viewModelScope在ViewModel中使用。

implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.2.0'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0'

Kotlin协程简介相关推荐

  1. Kotlin协程简介(一) Hello,coroutines!

    协程的作用 协程并不是一个新鲜概念,相信大家都有所了解,它的好处是可以极大程度的复用线程,通过让线程满载运行,达到最大程度的利用CPU,进而提升应用性能.它和反应式编程一样都可以有效的提高资源的利用率 ...

  2. Kotlin协程简介(一)

    目录: 一. 协程的基本概念 二. 从异步编程开始 回调 CompletableFuture RxJava 协程 三. 协程的基本概念 suspend funtion CoroutineScope C ...

  3. 探索 Kotlin 协程原理

    接下来跟大家分享一下我在了解 Kotlin 协程实现的过程中理解的一些概念,如果你发现哪些地方我说错了的话,欢迎提出你的理解. 1. Kotlin 协程原理概述 Kotlin 协程的大致的执行流程如上 ...

  4. Kotlin协程 - - - 协程的简单使用

    一.协程介绍 1.简介 协程是一种并发设计模式,您可以在 Android 平台上使用它来简化异步执行的代码.协程是在版本 1.3 中添加到 Kotlin 的,它基于来自其他语言的既定概念. 在 And ...

  5. 大型Android项目架构:基于组件化+模块化+Kotlin+协程+Flow+Retrofit+Jetpack+MVVM架构实现WanAndroid客户端

    前言:苟有恒,何必三更眠五更起:最无益,莫过一日曝十日寒. 前言 之前一直想写个 WanAndroid 项目来巩固自己对 Kotlin+Jetpack+协程 等知识的学习,但是一直没有时间.这里重新行 ...

  6. Kotlin 协程:简单理解 runBlocking, launch ,withContext ,async,doAsync

    前言 Kotlin的协程,本质上是一个线程框架,它可以方便的切换线程的上下文(如主线程切换到子线程/子线程切回主线程).而平时我们要想在Android Studio使用协程,先要在gradle引入协程 ...

  7. 在 Android 开发中使用 Kotlin 协程 (一) -- 初识 Kotlin 协程

    前言 最近在研究 Kotlin 协程,发现功能真的超级强大,很有用,而且很好学,如果你正在或计划使用 Kotlin 开发 Android,那么 Kotlin 协程你一定不能错过! 协程是什么? 我们平 ...

  8. 分析Kotlin协程只挂起不恢复会怎样(是否存在协程泄漏),以及挂起的协程存在哪里?

    前言 刚开始正式学协程原理的时候(以前只是学api怎么用),大概是20年6月,也就是bennyhuo大佬出书<深入理解Kotlin协程>的时候,我买了本然后细细研究,我的内心就一直有一个问 ...

  9. 动手实现Kotlin协程同步切换线程,以及Kotlin协程是如何实现线程切换的

    前言 突发奇想想搞一个同步切换线程的Kotlin协程,而不用各种withContext(){},可以减少嵌套且逻辑更清晰,想实现的结果如下图: 分析 实现我们想要的结果,首先需要知道协程为什么可以控制 ...

最新文章

  1. 前端学习(759):预解析案例
  2. BootStrap 用法
  3. 《机器学习训练秘籍》中文版58章节 完整开源 吴恩达
  4. java简述常见的布局极其特点_请简要说明 Android 中五种常见布局的特点。_学小易找答案...
  5. HoudahSpot 6.2 for Mac文件搜索软件
  6. 遥感学习教学课件分享
  7. JS 全局函数 isNaN()、parseInt()、parseFloat()
  8. python 三维矩阵乘以二维矩阵_python 二维矩阵转三维矩阵示例
  9. 【Unity3D】【UI】Cannot restructure Prefab instance.
  10. 用VSCode打开带图片的.md文件
  11. 从0到1 开启商业与未来的秘密读书笔记
  12. 谁抢走你的棒棒糖?精彩的创意让你得到的不仅仅是震撼 值得一看(图)
  13. 生物药、创新药、仿制药新项目立项调研、评估的重要手段
  14. R语言之空间数据操作
  15. bcc钱包地址生成linux,从Bcc到xdp原理分析
  16. 《Jave并发编程的艺术》学习笔记(1-2章)
  17. 加速度随机游走_VIO学习之公式推导——IMU篇
  18. NiiOS-iOS复习及在学习进度更新
  19. 视觉SLAM十四讲(一)基础知识
  20. 03、JCL(日志门面)

热门文章

  1. 老板为什么不愿意给你加工资?
  2. 图数据库中的空间值——实现图数据库内容在地理坐标系下展现
  3. 1044 火星数字 (20 分)
  4. 国家动物博物馆参观记
  5. 软件产品设计的学习总结
  6. Ubuntu安装LAMP
  7. 如何把删除的文件恢复
  8. ceph对接nfs-ganesha 安装配置手顺
  9. 智能机柜PDU助力中小型数据中心机房末端配电系统建设
  10. 什么是深拷贝?什么是浅拷贝?