kotlin协程原理

1 什么是协程 轻量级线程,kotlin在1.3版本后,提供了协程coroutine库,一种简化异步任务处理的方案。
2 为什么用协程 2.1 简化代码,增加可读性
使用协程可以用简洁直观可读性高的写法,实现多重依赖关系的异步任务的书写。
若不使用协程,一般的异步方式?

  • 通过Callback回调的方式
  • 利用AsyncTask
  • 通过链式调用
    java8提供的CompletableFuture
    使用RXJava这种链式实现多重依赖的异步任务,可以解决回调嵌套问题,相比于callback回调可读性好。
  • 新启线程
    .......
2.2 合理使用线程,减少性能损耗
  • 协程依赖于线程的使用,挂起协程不会阻塞线程,后续执行和复用,轻量级
  • 在协程默认的线程池中,处理逻辑更偏向在当前线程的任务队列中添加任务,而不是开启新的工作线程
  • 在多cpu情况下,会开启不超过cpu数的线程数并可以从其他线程的任务队列中抢夺任务,最大程度地复用已有的工作线程。
3 怎么使用协程 3.1 协程作用域
协程是一套管理和运行异步任务的框架,所以需要有运行的环境,也叫协程的作用域,在这个作用域里,才可以使用协程来执行异步任务。
(1) 全局环境
GlobalScope.launch {}
GlobalScope代表协程的全局作用域,在该作用域启动的协程为顶层协程,没有父任务,且该scope没有Job对象,所以无法对整个scope执行cancel()操作,
所以如果没有手动取消每个任务,会造成这些任务一直运行,可能会导致内存泄露等问题。
(2) 局部环境
CoroutineScope(Dispatchers.Main).launch {}
通常会通过创建CoroutineScope,来实现一个协程作用域,并且可以指定派发器,可以取消该scope下所有正在进行的任务。
3.2 协程派发器
kotlin提供的一些默认的Dispatcher:
名称 说明
Dispatchers.IO 工作线程池,依赖于Dispatchers.Default,支持最大并行任务数
Dispatchers.Main 主线程,这个在不同平台定义不一样,所以需要引入相关的依赖,比如Android平台,需要使用包含MainLooper的handler来向主线程派发
Dispatchers.Default Dispatchers.Default
Dispatchers.Unconfined 无指定派发线程,会根据运行时的上线文环境决定
通用的1和2
3.3 启动协程任务
(1) 对于一个scope对象,常用launch和async创建协程。
CoroutineScope(Dispatchers.IO).launch { }
CoroutineScope(Dispatchers.IO).async { }
而两者的最大不同是,async会创建一个Deferred的协程,可以用来等待该协程执行完毕再进行后续操作。
(2) 内部协程
CoroutineScope(Dispatchers.IO).launch { async { } }


在一个协程内部,也可以创建子协程。
(3)改变协程任务执行环境
如IO线程执行异步请求,数据回来后在主线程进行展示。
CoroutineScope(Dispatchers.IO).launch { val dataJob = async { //request data } async(Dispatchers.Main) { //prepare val data = https://www.it610.com/article/dataJob.await() //display with data } } }

5.协程挂起
协程可以顺序完成异步任务,那么在等待上一个协程任务完成时,当前的协程需要被挂起(不阻塞线程)
CoroutineScope(Dispatchers.IO).launch { val dataJob = async { //request data } withContext(Dispatchers.Main) { //prepare val data = https://www.it610.com/article/dataJob.await() //display with data } //do some post async job after display data async{ } }


withContext()方法,除了可以指定启动协程任务外,还可以挂起当前协程,即外部的launch的协程,直到withContext启动的协程任务完成后,才会重新恢复外部的launch协程,执行下面的async语句。
使用suspend关键字
CoroutineScope(Dispatchers.IO).launch { //prepare val data = https://www.it610.com/article/getData() //display with data } //挂起当前协程 private suspend fun getData() = suspendCoroutine { Call().addCallback(object : Callback { override fun onCall(res: String) { //恢复协程 it.resume(res) } }) }

使用suspend关键字,表明方法可以被挂起,称为挂起函数suspendCoroutine方法,会挂起当前的协程,并把挂起的协程返回到代码块的参数中,代码块执行自定义逻辑。
4协程实现原理 4.1 基本原理
三剑客:协程作用域,dispatcher,coroutine
简化结构关系:
kotlin协程原理
文章图片


详细关系图:
kotlin协程原理
文章图片

kotlin协程原理
文章图片

基本流程:在作用域启动协程,调度器调度,若协程挂起,则调度其他协程或者执行其他主协程代码
【kotlin协程原理】

    推荐阅读