启动模式
- DEFAULT:默认启动模式,协程会在创建后立即开始调度,如果在调度前协程被取消,其将直接进入取消响应的状态
- LAZY:懒启动模式,协程被调用才会启动执行,包括调用协程的start,join或者await等方法时才会被调度,如果调度前就取消那么该协程将直接进入异常结束状态
- ATOMIC:原子启动模式,立即开始调度,在执行第一个挂起点之前协程不能被取消
- UNDISPATCHED:不受限的启动模式,协程创建后立即在当前函数调用栈中执行,直到遇到第一个真正挂起的点才会切线程
立即调度:表示协程的调度器会立即接收调度指令,但具体执行的时机以及在哪个线程上执行,还需要根据调度器的具体情况而定
调度器
- Default:默认调度器,适合处理后台计算,是一个CPU密集型任务调度器
- Main:UI调度器,平台相关,在Android上是主线程
- Unconfined:无所谓调度器,挂起点恢复执行时会在恢复所在的线程上直接执行,如A(创建协程的线程)-B(协程挂起的线程)-B(协程恢复后执行的线程)
- IO:IO调度器,适合执行IO相关操作,是一个IO密集型任务调度器
上下文 CoroutineContext
- 可通过实现AbstractCoroutineContextElement抽象类自定义上下文
- 常用的有AndroidExceptionPreHandler、CoroutineName
- Job的作用,没Job实例的协程不能被取消
拦截器
可作为上下文的一部分
可以拦截协程异步回调时的恢复调用,即可以在挂起点恢复执行的位置添加拦截器实现AOP操作。
作用域
作用域的概念,主要用以明确协程之间的父子关系,以及对于取消或者异常处理等传播行为。
协程作用域包括以下三种:
- 顶级作用域:没有父协程的协程所在的作用域为顶级作用域
- 协同作用域:协程中启动新的协程,新协程为所在协程的子协程,这种情况下子协程所在的作用域默认为协同作用域。子协程抛出的未捕获异常都将传递给父协程处理,父协程同时也会被取消
- 主从作用域:与协同作用域在协程的父子关系上一致,区别在于处于该作用域下的协程出现未捕获的异常时不会将异常向上传递给父协程
主从作用域可通过阻断子协程未捕获的异常向上传播实现,如supervisorScope()
除这三种作用域中提到的行为外,父子协程之间还存在以下规则:
- 父协程被取消,所有子协程均被取消
- 父协程需要等待子协程执行完毕之后才能最终进入完成状态
- 子协程会继承父协程的协程上下文中的元素,如果自身有相同的key成员,则覆盖父协程对应的key,覆盖效果仅限子协程作用域
启动协程的方式
launch
- 返回Job对象,但无法获取协程运行结果
async
- 返回Deffer对象,可以获取协程运行结果
Deffer中await()方法的作用:
- 在协程已经执行完成时,立即返回协程执行的结果,如果协程异常结束,则抛出异常
- 如果协程尚未执行完成,则挂起直到协程执行完成,与join类似
挂起与恢复
挂起的本质是程序执行流程发生异步调用时,当前调用流程的执行状态进入等待状态。
异步调用发生与否,取决于恢复调用函数与对应的挂起函数的调用是否在相同的调用栈上,切换函数调用栈的方法可以是切换到其他线程上执行,也可以是不切换线程但在当前函数返回之后的某一个时刻再执行。
- 协程内部挂起函数的调用处被称为挂起点,挂起点出现异步调用则当前协程被挂起,直到对应的Continuation的恢复调用函数被调用才会恢复执行
- 挂起标记 - return非阻塞式挂起当前线程
- invokeSuspend、resume - 恢复调用
挂起的本质
常用
suspendCoroutine
- 可处理异步回调的逻辑
suspendCancellableCoroutine
- 可在协程被取消时收到回调 CancellableContinuation#invokeOnCancellation
Channel
Flow
- 冷流:有观察者消费时才执行
- 热流:马上执行
Select
参考文章:Kotlin协程