kotlin协程问题
一:拦截器可以有多个吗
不可以,因为CoroutineContext在组合在一起的时候,是从左往右累加的,具有相同Key值的Element,左边的会丢弃。而拦截都继承的
ContinuationInterceptor
它内部代码是这样的
public interface ContinuationInterceptor : CoroutineContext.Element { companion object Key : CoroutineContext.Key。。。。 }
也就是说拦截器的Key值是相同的。
二:协程的官方解释
协程(英语:coroutine)是计算机程序的一类组件,推广了协作式多任务的子例程,允许执行被挂起与被恢复。
协作式多任务和多线程中的抢占式多任务相对,子例程就是方法或函数。那么是不是可以说,协程就是这样一个框架: 控制若干个方法合作式的执行。
三:job的本质
Job对象具有lifecycle生命周期属性, 协程之间具有父子关系,父协程在所有子协程完成之后,状态才会为finished。悟:Job用来控制生命周期!
四:coroutineContext的本质
CoroutineContext是一个拥有很多element,这些element可以控制协程行为,有几个默认的coroutineContext,他们有默认值。我有点悟到的一点是,CoroutineContext,协程上下文,本质就是用来控制协程的行为的,比如如何调度,如何控制异常,名字是什么等等。
子协程会从父协程继承CoroutineContext,规则如下
我们来看一下上下文的继承规律
val scope = CoroutineScope(Dispatchers.IO+CoroutineName("父协程")+Job()) scope.launch(CoroutineName("子协程1")) { println("key: ${this.coroutineContext[CoroutineDispatcher.Key]}") println("Job : ${this.coroutineContext[Job.Key].hashCode()}") println("name: ${this.coroutineContext[CoroutineName]}") println("----------") }.join() scope.launch(Dispatchers.Default){ println("key: ${this.coroutineContext[CoroutineDispatcher.Key]}") println("Job : ${this.coroutineContext[Job.Key].hashCode()}") println("name: ${this.coroutineContext[CoroutineName]}") println("----------") }.join() scope.launch(CoroutineName("子协程3")){ println("key: ${this.coroutineContext[CoroutineDispatcher.Key]}") println("Job : ${this.coroutineContext[Job.Key].hashCode()}") println("name: ${this.coroutineContext[CoroutineName]}") println("----------") }.join()
结果如下
key: Dispatchers.IO Job : 1757380160 name: CoroutineName(子协程1) ---------- key: Dispatchers.Default Job : 444126595 name: CoroutineName(父协程) ---------- key: Dispatchers.IO Job : 331507349 name: CoroutineName(子协程3) ---------- Process finished with exit code 0
- 子协程的上下文会覆盖父协程的上下文
- 子协程会有一个独立的Job对象实例,其实想一下也很清楚,因为Job对象是控制这个协程的生命周期的,自然不能混用
- 子协程之间互不影响,比如第一个块把name修改,并不会影响第二个块从父协程继承来的name。第二个块对调度器修改并不会影响第三个块继承来的调度器。
五:协程的取消
悟:cancle a scope!!! cancle all its children. 取消一个子协程,不会影响其他同级的子协程。
这就是协程结构化并发的两个特点:
- 取消一个协程作用域,将取消该协程作用域下的所有子协程
- 被取消的子协程,不会影响其它同级的协程
在Android开发中,大部分场景下我们不需要考虑协程的cancel,借助ViewModelScope、LifecycleScope和MainScope这些场景的协程作用域,我们可以很方便的避免内存泄漏,在cancel时结束所有的子协程。
协程的取消是”合作式“的,就像thread被interrupt之后,要在任务逻辑中加一个判断是否被interrupt一样,协程代码块中应该加一个isActive判断或ensureActive();有区别。isActive允许你在知道被取消之后还做点什么,ensureActive直接结束,虽然代码中有throw exception,但并没有抛出异常,我也不知道!!
yield也可以做到ensureActive的效果,但它本意是让步,具体看文档。只不过当这个yield没有去让步去挂起的时候,总是会检查是否被取消,来达到我们取消协程执行的目的。
delay内部有检查取消的逻辑