Kotlin协程作用域与Job详解
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
fun main() = runBlocking { //直接将runBlocking函数返回给main函数
GlobalScope.launch {
delay(1000)
println("Kotlin Coroutines")
}
println("Hello")
delay(2000)
println("World")
}
有木有一种机制能够精确的等待协程执行完了,再执行主线程呢?
所以咱们将delay去掉,需要改造一下,先把主线程的delay给注释掉:那怎么做呢?这里先来看一下GlobalScope.launch()方法的返回值:
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
}
返回值为Job
回顾一下之前Job的理论描述:
Job & Deferred:
- Job,任务,封装了协程中需要执行的代码逻辑。Job可以取消并且有简单的生命周期,它有如下几种状态:
- Job完成时是没有返回值的,如果需要返回值的话,应该使用Deferred,这是Job的子类。
public interface Deferred<out T> : Job
一个背后的job,从概念上讲,一个job是一个具有生命周期并可以取消的东西,它可以标识着它的完成状态。
那能拿到Job是不是可以通过它来达到我们精准等待协程执行完的目的呢?所以首先咱们来获取它的返回值:
val myJob = GlobalScope.launch {
delay(1000)
println("Kotlin Coroutines")
}
那重点就是怎么通过这个Job来达到我们的目的了, 其实它里面有一个跟咱们线程中一样的join()方法,不过实现肯定不同啦,瞅瞅:
Suspends the coroutine until this job is complete.挂起协程,直到此工作完成。
import kotlinx.coroutines.*
fun main() = runBlocking {
val myJob = GlobalScope.launch {
delay(1000)
println("Kotlin Coroutines")
}
println("Hello")
myJob.join()
println("World")
}
RUN>
Hello Kotlin Coroutines World Process finished with exit code 0
确实是如此,这次“Kotlin Coroutines”和"World"几乎是同时间输出了。
协程作用域
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
fun main() {
//CoroutineScope.launch{}:
//CoroutineScope.launch{}是最常用的Coroutine builders,不阻塞当前线程,在后台创建一个新协程,也可以指定协程调度器。
//GlobalScope 协程构建器
GlobalScope.launch {
delay(1000)//此时不会阻塞线程,当指定的时间过后协程会恢复执行
println("Kotlin Coroutines")
}
println("Hello")
Thread.sleep(500)//----->
println("World")
}
也就是我们用GlobalScope.lauch()构造的协程中的代码是依附于主线程,如果主线程退出了,其协程也退出了.
看下面这个代码就会颠覆这个效果了,如下:
fun main() = runBlocking {
//CoroutineScope.launch{}是最常用的Coroutine builders,不阻塞当前线程,在后台创建一个新协程,也可以指定协程调度器。
launch {//----> 直接调用launch 非GlobalScope.launch
delay(1000)
println("Kotlin Coroutines")
}
println("Hello")
}
RUN> ??????
Hello Kotlin Coroutines Process finished with exit code 0
发现此时“hello”在输出之后,还在等待后台协程在“Kotlin Coroutines”输出完之后再退出,这是为啥呢?这里就涉及到了“协程作用域”的概念好,下面先来看下理论:
“每一个协程构建器(包括runBlocking)都会向其代码块作用域中添加一个CoroutineScope实例,我们可以在该作用域中启动协程,而无需显示将其join到一起,这是因为外部协程(在上面的示例中就是runBlocking)会等待该作用域中的所有启动的协程全部完成后才会完成。”
/*
每一个协程构建器(包括runBlocking)都会向其代码块作用域中添加一个CoroutineScope实例。我们可以在该作用域中启动协程,而无需
显式将其join到一起,这是因为外部协程(在下面的示例中就是runBlocking)会等待该作用域中的所有启动的协程全部完成后才会完成。
*/
fun main() = runBlocking {
//CoroutineScope.launch{}是最常用的Coroutine builders,不阻塞当前线程,在后台创建一个新协程,也可以指定协程调度器。
launch {
delay(1000)
println("Kotlin Coroutines")
}
println("Hello")
}
回到之前的这段代码:
而如果不用GlobalScope,而直接调用launch,它其实创建的Scope就是runBlocking所创建的CoroutineScope实例了,此时外部的协程就会等待里面的协程了,还是比较难理解的,所以我们就可以利用这个特性来比较巧妙的来达到文章开头想要实现的那个效果。