Android—Kotiln进阶教程(android kotlin教程)
前言
在上一篇中对Kotlin协程里的调度器进行对应的讲解。在本篇中,将会对Kotlin协程对应上下文相关的知识点进行讲解。
1、上下文中的作业
协程的 Job 是上下文(CoroutineContext )的⼀部分,并且可以使⽤ coroutineContext [Job] 表达式在上下⽂中检索它:
fun main() = runBlocking<Unit> { val job = launch { println("My job is ${coroutineContext[Job]}") //isActive } println(coroutineContext[Job]?.isActive) println(job.isActive) } 复制代码
CoroutineScope
中的 isActive 只是 coroutineContext[Job]?.isActive == true
的⼀种方便的快捷方式。
还记得之前的取消协程的判断不?就是通过判断上下文里面的isActive
来检查取消的!
2、子协程
当⼀个协程被其它协程在 CoroutineScope
中启动的时候,它将通过CoroutineScope.coroutineContext
来承袭上下文,并且这个新协程的 Job 将会成为⽗协程作业的子作业(子协程)
来看看例子
// 启动⼀个协程来处理某种传⼊请求(request fun main() = runBlocking<Unit> { //CoroutineScope val request = launch { // 孵化了两个⼦作业, 这个通过 GlobalScope 启动,这个不是子协程! GlobalScope.launch { //CoroutineScope log("job1: I run in GlobalScope and execute independently!") delay(2000) //这里挂起了2秒,再执行最后打印 log("job1: I am not affected by cancellation of the request") } // 这个则承袭了⽗协程的上下⽂ ,这个就是子协程! launch { //CoroutineScope delay(100) log("job2: I am a child of the request coroutine") delay(2000) //这里挂起了2秒,再执行最后打印 log("job2: I will not execute this line if my parent request is cancelled") } } delay(500) //挂起500毫秒就取消 request.cancel() delay(5000) //这里为了不让程序过早退出,所以等待5秒, log("main: Who has survived request cancellation?") } 复制代码
这里用上了上一篇的日志打印。
来看看运行效果
[DefaultDispatcher-worker-1] job1: I run in GlobalScope and execute independently! [main] job2: I am a child of the request coroutine [DefaultDispatcher-worker-1] job1: I am not affected by cancellation of the request [main] main: Who has survived request cancellation? 复制代码
通过这个运行效果我们可以看出:
当⼀个父协程被取消的时候,所有它的子协程也会被递归的取消。
当使用 GlobalScope 来启动⼀个协程时,则新协程的作业没有⽗作业,
因此它与这个启动的作用域无关,且独立运作;
所以说,当外部协程取消时,它并未受任何影响,直到运行完成。
3、父协程的职责
fun main() = runBlocking<Unit> { val request = launch { repeat(3){ i -> launch { delay((i + 1) * 200L) println("Coroutine $i is done") } } println("request: I'm done and I don't explicitly join my children that are still active") } // request.join() println("Now processing of the request is complete") } 复制代码
运行效果
Now processing of the request is complete request: I'm done and I don't explicitly join my children that are still active Coroutine 0 is done Coroutine 1 is done Coroutine 2 is done 复制代码
从这个运行效果可以看出:
当未使用
job.join()
时,父协程将会等待所有的子协程执行结束;当使用
job.join()
时,父协程将会等待所有子协程执行结束后,然后执行主协程的下一句(这个之前demo体验过,在这就不过多演示了,读者可以亲自尝试)
4、命名协程方便调试
这个很简单,直接上例子
fun main() = runBlocking<Unit> { log("Started main coroutine") val v1 = async(CoroutineName("v1coroutine")){ delay(500L) log("Computing v1") 252 } val v2 = async(CoroutineName("v2coroutine")){ delay(1000L) log("Computing v2") 6 } log("The answer for v1 / v2 = ${v1.await() / v2.await()}") } 复制代码
注意:这里使用了CoroutineName("v2coroutine")
将对应的协程命名为v2coroutine
。
来看看运行效果
[main] Started main coroutine [main] Computing v1 [main] Computing v2 [main] The answer for v1 / v2 = 42 复制代码
嗯?发现并没有 将对应协程的命名给打印出来!
如果读者也是这个结果,这时需要配置一下了。
如图所示
在这加上:-Dkotlinx.coroutines.debug=on
再次运行试试:
[main @coroutine#1] Started main coroutine [main @v1coroutine#2] Computing v1 [main @v2coroutine#3] Computing v2 [main @coroutine#1] The answer for v1 / v2 = 42 复制代码
4.1 组合上下文元素命名
fun main() = runBlocking<Unit> { launch(Dispatchers.Default + CoroutineName("test")) { println("I'm working in thread ${Thread.currentThread().name}") } } 复制代码
狠简单,在对应子协程里面加入了test命名。
看看运行效果
I'm working in thread DefaultDispatcher-worker-1 @test#2 复制代码
结束语
好了,本篇到这里就结束了,同时对应的进阶教程也讲完了。以后将会以实战的方式来巩固Kotlin对应的知识点。
作者:hqk
链接:https://juejin.cn/post/7032951566042660872