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) 内部协程
|
在一个协程内部,也可以创建子协程。
(3)改变协程任务执行环境
如IO线程执行异步请求,数据回来后在主线程进行展示。
|
5.协程挂起
协程可以顺序完成异步任务,那么在等待上一个协程任务完成时,当前的协程需要被挂起(不阻塞线程)
|
withContext()方法,除了可以指定启动协程任务外,还可以挂起当前协程,即外部的launch的协程,直到withContext启动的协程任务完成后,才会重新恢复外部的launch协程,执行下面的async语句。
使用suspend关键字
|
使用suspend关键字,表明方法可以被挂起,称为挂起函数suspendCoroutine方法,会挂起当前的协程,并把挂起的协程返回到代码块的参数中,代码块执行自定义逻辑。
4 协程实现原理
4.1 基本原理
三剑客:协程作用域,dispatcher,coroutine
简化结构关系:
详细关系图:
基本流程:在作用域启动协程,调度器调度,若协程挂起,则调度其他协程或者执行其他主协程代码