协程上下文与Job深入解析
在上一次https://www.cnblogs.com/webor2006/protected/p/12611978.html对于协程的调试与线程之间的切换进行了相关的学习,这次进一步对Job进行进一步的学习,在之前https://www.cnblogs.com/webor2006/p/11730528.html其实已经对Job有了一定的了解,简单回顾一下:
当时咱们举的一个等待协程代码执行完利用到了Job.join()这个方法的例子, 其实关于Job还有更加深入的东西待挖掘,下面来看一下。
Job深入解析:
在上次的咱们使用Job的例子中是通过调用"GlobalScope.launch()"来获得Job对象的:
因为GlobalScope.launch()本身就是返回的Job对象:
但是!!对于没有明确返回值Job的场景那又有什么方法能获取到Job本身呢?比如说:
这个runBlocking的函数的返回值并非是Job的:
此时先来上一个理论说明:
“协程的Job是归属其上下文(Context)的一部分,Koltin为我们提供了一种简洁的手段来通过协程上下文获取到协程自身的Job对象。我们可以通过coroutineContext[Job]表达式来访问上下文中的Job对象。”
还是有些抽象,下面用例子来进行说明:
运行看一下:
此时咱们可以添加上一次https://www.cnblogs.com/webor2006/protected/p/12611978.html学习的协程调试的JVM参数再来看一下协程的信息:
再运行:
程序虽简单,但是这里面蕴藏的细节还是相当多的,目前而言存在如下几个疑点:
1、coroutineContext从哪来的,为啥可以直接使用?
2、为啥可以直接使用[]来调用,背后我猜肯定是用了运算符重载了,实际是否如猜想呢?
3、 为啥咱们可以直接使用这个Job,它又是哪里来的?
4、为啥输出结果是它?
以上四个问题,下面则来一一进行剖析。
剖析Job协程上下文的原理:
接下来则从上面的四个疑问的问题寻找其整个背后的原理。
疑问一:coroutineContext从哪来的,为啥可以直接使用?
点击进去查看一下它:
原来它是在CoroutineScope接口里所定义的一个属性, 而CoroutineScope接口有很多具体的实现类,根据咱们的输出结果来看看到了一个“BlockingCoroutine”,它就是具体的子类之一:
打开瞅一下这个具体类:
而这个context的值又是来自于上面这两个属性来构成:
而runBlocking()里面的代码块就是运行在CoroutineScope中的:
很明显咱们就可以在这个协程代码块中直接使用coroutineContext了:
咱们再来读一下该属性的说明:
从文档说明中也能看到,代码中要访问这个属性唯一的使用就是访问Job,所以正好是咱们目前所使用的代码形式,目前此问题就已经解决了。
疑问二:为啥可以直接使用[]来调用,背后我猜肯定是用了运算符重载了,实际是否如猜想呢?
这里还是从源码来找答案:
此时看一下CoroutineContext这个类:
而看它里面有一个操作符的重载,如我们所猜想:
所以这也是为啥咱们是用的可空类型来接收:
关于operator fun的写法在之前https://www.cnblogs.com/webor2006/p/11369333.html的属性委托有过使用,而之所以可以使用[]表达式来访问,这个语法在之前倒还木有学到,这里参考一下这位博主的说明:https://www.jianshu.com/p/e265dbcfe009,其中看一个例子就明白了:
而我们访问时就可以这样:
回到咱们这个重载函数的定义:
很明显咱们也可以用[]来访问这个重载符函数,其中接收的参数是一个Key类型,所以最终我们看到这样的写法了:
此问题就已经明白了。
疑问三:为啥咱们可以直接使用这个Job,它又是哪里来的?
接下来解决最后一个疑点,既然我们在第二个问题分析中可以看出实际是要传一个Key类型的,但是为啥我们传了一个Job呢?
Job和Key之间肯定有联系,下面来剖析一下:
咱们先来看一下Key的定义:
好,此时再来看一下我们调用时传递的Job的定义:
很明显,我们直接能传递这个Job的原因就是由于在里面定有伴生对象所达成的,关于伴生对象可以参考https://www.cnblogs.com/webor2006/p/11210181.html,这块的答案就可以揭晓了。
疑问三:为啥输出的Job是BlockingCoroutine?
点击瞅一下它的定义:
而这个BlockingCoroutine的继承体系如下:
所以,最后这个疑点也解决了。
最后再来修改一下程序:
然后咱们来看一下这个isActive的定义:
而从它的实现细节就可以看到确实如文档所说:
咱们来试一下:
而这个的isActive是定义在Job里面的:
那,研究这个细节有啥意义呢?结论就是:对于协程是否是isActive其实是取决于它的Job是不是isActive。
以上则对于这个非常简单的例子来完整的阐述了它里面背后的原理,虽说程序简单,但是要搞清楚其背后机制还是不简单的,通过这样的一个彻底分析,对于协程、协程上下文、Job之间的关系也更进一步的。