协程作用域的创博是子协程默认使用的是父协程的作用域,这么做的好处能更好的管理所有子协程,下面再举个例子
scss
@JvmStatic
fun main(a:Array<String>) {
runBlocking {
launch {
delay(1000)
println("-------end runBlocking launch--------")
}
launch {
delay(1500)
println("-------end runBlocking with new Dispatchers launch--------")
}
}
}
-------end runBlocking launch--------
-------end runBlocking with new Dispatchers launch--------
Process finished with exit code 0
在这段代码中我们使用 runBlocking 创建了一个 协程,同时在 runBlocking 中使用launch 创建了2个子协程,
从这个例子可以看出来不管子协程刮起了多久,父协程都会等待子协程执行完成,
那么什么是协程作用域呢
这段代码中的 CoroutineScope 就是协程作用域,那么如果我们在 runBlocking 中创建一个新的协程作用域,看看他的表现又是如何
scss
@JvmStatic
fun main(a:Array<String>) {
runBlocking {
launch {
delay(1000)
println("-------end runBlocking launch--------")
}
launch {
delay(1500)
println("-------end runBlocking with new Dispatchers launch--------")
}
GlobalScope.launch{
delay(2000)
println("-------end new CoroutineScope launch--------")
}
}
}
结果:
-------end runBlocking launch--------
-------end runBlocking with new Dispatchers launch--------
Process finished with exit code 0
可以看到如果使用新的作用域的子协程如果耗时是最长的,那么外部协程是不会等待这个协程的
为什么说 GlobalScope.launch 是一个新的协程作用域 而上面的 launch 则不是呢,我们先来看一下 runBlocking 方法
kotlin
public actual fun <T> runBlocking(context: CoroutineContext, block: suspend CoroutineScope.() -> T): T {
...省略
}
由于我们使用 lambda 表达式 runBlocking 就变成了上面的样子,下面我们使用正常的用法来写这个 runBlocking
kotlin
@JvmStatic
fun main(a:Array<String>) {
runBlocking(block = {
launch {
delay(1000)
println("-------end runBlocking launch--------")
}
launch {
delay(1500)
println("-------end runBlocking with new Dispatchers launch--------")
}
})
}
可以看到 runBlocking 大括号内的方法就是 block 方法,而block 方法是 CoroutineScope 的匿名扩展方法,最后我们 runBlocking 的协程作用域就是我们常见的这个 CoroutineScope, 下面第一个 launch 方法又如何解释呢
kotlin
public fun CoroutineScope.launch(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit
): Job {
val newContext = newCoroutineContext(context)
val coroutine = if (start.isLazy)
LazyStandaloneCoroutine(newContext, block) else
StandaloneCoroutine(newContext, active = true)
coroutine.start(start, coroutine, block)
return coroutine
}
launch 方法是 CoroutineScope 的扩张方法,只要我们能获取到 CoroutineScope ,就能直接使用
由于这个方法是 CoroutineScope 的匿名扩展方法,所以他可以用 CoroutineScope 内的所有方法,再将 launche 方法前面加上this ,就是他的原始模样,
kotlin
@JvmStatic
fun main(a:Array<String>) {
runBlocking(block = {
this.launch {
delay(1000)
println("-------end runBlocking launch--------")
}
this.launch {
delay(1500)
println("-------end runBlocking with new Dispatchers launch--------")
}
})
}
所以在不重新指定新的作用域的情况下, 子协程默认使用的是父协程的作用域
说完了协程作用域的传播再来看看取消后 job 状态的变化
先来看一下协程启动前与协程执行完成后的状态变化
kotlin
@JvmStatic
fun main(a:Array<String>) {
runBlocking(block = {
var job= this.launch {
delay(1000)
println("-------end runBlocking launch--------")
}
println("job isActive:${job.isActive} isCancelled:${job.isCancelled} isCompleted:${job.isCompleted}")
job.join()
println("job isActive:${job.isActive} isCancelled:${job.isCancelled} isCompleted:${job.isCompleted}")
})
}
结果:
job isActive:true isCancelled:false isCompleted:false
-------end runBlocking launch--------
job isActive:false isCancelled:false isCompleted:true
Process finished with exit code 0
各个状态看起来都比较正常,我们再看一下子协程未启动前取消他的状态变化
kotlin
@JvmStatic
fun main(a:Array<String>) {
runBlocking(block = {
var job= this.launch {
delay(1000)
println("-------end runBlocking launch--------")
}
job.cancel()
println("job isActive:${job.isActive} isCancelled:${job.isCancelled} isCompleted:${job.isCompleted}")
delay(2000)
println("job isActive:${job.isActive} isCancelled:${job.isCancelled} isCompleted:${job.isCompleted}")
})
}
结果:
job isActive:false isCancelled:true isCompleted:false
job isActive:false isCancelled:true isCompleted:true
Process finished with exit code 0
在协程没有启动前就取消了协程,看到子协程的变化由原来的活跃状态变为了 取消状态,但是等到子协程挂起恢复后的时间节点后,子协程又变为了 已完成同时也是已取消的状态,这个就比较有意思了