关于android:CoroutineContext源码分析

6次阅读

共计 14803 个字符,预计需要花费 38 分钟才能阅读完成。

context,即协程上下文

public interface Continuation<in T> {
    /**
     * The context of the coroutine that corresponds to this continuation.
     */
    public val context: CoroutineContext

    /**
     * Resumes the execution of the corresponding coroutine passing a successful or failed [result] as the
     * return value of the last suspension point.
     */
    public fun resumeWith(result: Result<T>)
}

根本应用

本文次要剖析 CoroutineContext 实现原理,并不会写太多应用上的内容。

CoroutineContext.Element有一个抽象类 AbstractCoroutineContextElement 有了它创立一个元素就很简略了,间接提供 Key 即可。

集体了解这种设计形式相似AbstractLis,利用一个抽象类,对接口的一些专用、高频办法进行一些默认的实现。有非凡需要独自实现。这样继承它用起来比拟不便

//Base class for [CoroutineContext.Element] implementations.
abstract class AbstractCoroutineContextElement{public override val key: Key<*>}: Element

创立元素并不难,提供对应的 Key 即可,咱们能够利用它来实现一个协程名

class CoroutineName(val name: String): AbstractCoroutineContextElement(CoroutineName.Key){
  companion object Key: CoroutineContext.Key<CoroutineName>// 伴生对象
  override fun toString(): String = "CoroutineName($name)"
}

把定义好的协程上下文赋值给作为实现回调的 Continuation(completion)实例,这样就能够将它绑定到协程上了。

fun test(){
  suspend{// 协程体...}.startCoroutine(MyContinuation())
}

class MyContinuation() : Continuation<Int> {
    // 绑定上下文,这里我成心设置两个 name,我就是玩儿
    override val context: CoroutineContext = CoroutineName("Co-01") + CoroutineName("Co-02")
    override fun resumeWith(result: Result<Int>) {context[CoroutineName].toString()// 上下文应用}
}

在我学习上下文的时候,当我看到 context[CoroutineName]CoroutineName("Co-01") + CoroutineName("Co-02")我立刻意识到它重载了操作符,立马点开 CoroutineContext 源码,当场拘捕如下两个办法

public operator fun <E : Element> get(key: Key<E>): E?

public operator fun plus(context: CoroutineContext): CoroutineContext

不晓得你们想到了吗?如果没想到阐明对 kotlin 语法还不太熟悉哦 ~


当初能够间接拉到文章底部看总结的论断

从这里开始,全是论断的源码证实。

CoroutineContex 剖析

伪代码构造

public interface CoroutineContext {// 重载 get 实现 context[key]用法     
     public operator fun <E : Element> get(key: Key<E>): E?
        
     public fun <R> fold(initial: R, operation: (R, Element) -> R): R
     // 重载 plus 实现 +,返回值是 CoroutineContext 类型。//override val context: CoroutineContext = CoroutineName("Co-01") + CoroutineName("Co-02")
     // 这个办法十分要害,上面会具体解读
     public operator fun plus(context: CoroutineContext): CoroutineContext...
     // 返回一个去除链表中 key 的对象的上下文。public fun minusKey(key: Key<*>): CoroutineContext
     //[CoroutineContext] 元素的键。[E] 是一种具备此键的元素。E 必须是 Element 的子类型,约等于 E extends Element.
     public interface Key<E : Element>
    
     // Element 是上下文的元素接口,这里挺有意思它居然也实现了 CoroutineContext
     public interface Element : CoroutineContext {
        /**
         * 一个形象属性,让子类必须提供的,就是 key 么。标识
         */
         public val key: Key<*>
        // 以下均势子类的实现
         public override operator fun <E : Element> get(key: Key<E>): E? =
            @Suppress("UNCHECKED_CAST")
            if (this.key == key) this as E else null

         public override fun <R> fold(initial: R, operation: (R, Element) -> R): R =
            operation(initial, this)

         public override fun minusKey(key: Key<*>): CoroutineContext =
            if (this.key == key) EmptyCoroutineContext else this
     }
}

下面就是预热一下,对这个接口的各个局部有个大略理解,看不懂没关系,上面会有残缺、零碎的源码剖析。

简略总结一下:尽管 Element 也是 CoroutineContext 子类,然而它也重写了 CoroutineContext 的办法,它跟 CoroutineContext 间接实现类是有显著区别的。

上下文数据结构

CombinedContext

internal class CombinedContext(
    private val left: CoroutineContext,
    private val element: Element
) : CoroutineContext, Serializable {override fun <E : Element> get(key: Key<E>): E? {
        var cur = this
        while (true) {cur.element[key]?.let {return it}
            val next = cur.left
            if (next is CombinedContext) {cur = next} else {return next[key]
            }
        }
    }

    public override fun <R> fold(initial: R, operation: (R, Element) -> R): R =
        operation(left.fold(initial, operation), element)

    public override fun minusKey(key: Key<*>): CoroutineContext {// 如果 element[key]不等于空,即 key 相等,阐明 element 被命中了,element 须要被摈弃,let 是内联函数,所以等效于间接返回 left。否则持续向下递归执行
        // 题外话:这就阐明为啥拦截器放在队尾,因为这样间接就返回了,效率很高省去迭代。当然不是在这里 get,然而情理是一样的。顺便提一嘴。element[key]?.let {return left}
        val newLeft = left.minusKey(key)// 递归到最原始的节点的 left 就是 Element,如果命中相等就是 EmptyCoroutineContext。// 假如咱们递归至最原始的 left,left 命中了。return when {
            newLeft === left -> this// 如果没命中
            // 如果链表尾部命中,返回 element 即可。newLeft === EmptyCoroutineContext -> element
            // 如果两头,或者头部命中。把断开的链子接上。else -> CombinedContext(newLeft, element)
        }
    }

    private fun size(): Int {
        var cur = this
        var size = 2
        while (true) {
            cur = cur.left as? CombinedContext ?: return size
            size++
        }
    }

    private fun contains(element: Element): Boolean =
        get(element.key) == element

    private fun containsAll(context: CombinedContext): Boolean {
        var cur = context
        while (true) {if (!contains(cur.element)) return false
            val next = cur.left
            if (next is CombinedContext) {cur = next} else {return contains(next as Element)
            }
        }
    }

    override fun equals(other: Any?): Boolean =
        this === other || other is CombinedContext && other.size() == size() && other.containsAll(this)

    override fun hashCode(): Int = left.hashCode() + element.hashCode()

    override fun toString(): String =
        "[" + fold("") { acc, element ->
            if (acc.isEmpty()) element.toString() else "$acc, $element"} + "]"
}

CombinedContextCoroutineContext 的实现类

实现 CoroutineContext接口代表列表,实现 CoroutineContext.Element 的是增加到列表里的元素

CombinedContext 是一个递归列表,跟咱们常常用的 List Node 节点还有点不太一样。left指向上一个节点,但他们是嵌套关系,element代表以后元素

通过这幅图,要试着了解这种”嵌套“或者说”包裹“的关系。

实例 2 能够了解为之前原有的上下文对象。My5CoroutineName能够了解须要新插入列表中的 Element 实例 2 .plusMy5CoroutineName后,创立并返回了 实例 1 ,新增节点把老节点包起来存在left。跟双链表那种插入元素批改指针指向的形式有显著区别。

EmptyCoroutineContext

还有这个数据结构,空上下文。

public object EmptyCoroutineContext : CoroutineContext, Serializable {
    private const val serialVersionUID: Long = 0
    private fun readResolve(): Any = EmptyCoroutineContext

    public override fun <E : Element> get(key: Key<E>): E? = null
    public override fun <R> fold(initial: R, operation: (R, Element) -> R): R = initial
    public override fun plus(context: CoroutineContext): CoroutineContext = context
    public override fun minusKey(key: Key<*>): CoroutineContext = this
    public override fun hashCode(): Int = 0
    public override fun toString(): String = "EmptyCoroutineContext"}

残缺代码剖析

开始之前,再来重温一下这段代码

class MyContinuation() : Continuation<Int> {override val context: CoroutineContext = CoroutineName("Co-01") + CoroutineName("Co-02")
    override fun resumeWith(result: Result<Int>) {context[CoroutineName].toString()// 上下文应用}
}

首先必定是不能不初始化context,所以要么是EmptyCoroutineContext,要么是Element

其次 CoroutineNameElement 类型,CombinedContext 公有 类。咱们 不能结构 一个 CombinedContext 赋值给 context。

长期论断

上下文列表的第一个 CombinedContext 的 left 肯定是 Element

之所以定这个论断,是因为前面波及到很多递归代码,所以必须把完结递归的条件搞清楚。

残缺代码

咱们从下面的例程登程,来摸索一下 CoroutineContext 的数据结构, 此次剖析代号K1.

案例 K1



public interface CoroutineContext {
    
    // 有三处实现,别离是:Element、CombinedContext、EmptyCoroutineContext
    public operator fun <E : Element> get(key: Key<E>): E?

    // 有三处实现,别离是:Element、CombinedContext、EmptyCoroutineContext
    public fun <R> fold(initial: R, operation: (R, Element) -> R): R

    // 只有这一处实现
    // 留神:假如 CoroutineName("Co-01") + CoroutineName("Co-02") 缩写为:co1.plus(co2)那么上面的 this 指的是 co1  context 指的是 co2
    public operator fun plus(context: CoroutineContext): CoroutineContext =
         // 传入的元素是空那么没必要拼接间接返回以后(this)if (context === EmptyCoroutineContext) this else // fast path -- avoid lambda creation
            //1, 这里开始代码很芜杂,不要急,一步一步得捋。必定能轻松看明确
            //2. 首先花 2 分钟去下面认真看 fold 函数的定义,如果看不懂,把 CoroutineContext 当成 R 类型带入,你就明确上面传 this 的目标了。//3. 这里简略剖析一下第一个参数要求是 CoroutineContext 的对象,这里传了 [this] 就是以后的上下文对象
            // 第二个参数是一个函数,所以依据 Lambda 简化规定,能够间接跟着{...}. 两个参数别离是 acc: CoroutineContext,element: Element
            //4. 想必你也猜出来了,我罗唆再强势一点,间接通知你 acc 代表[left],element 对应 [element]
            //5. 留神,这里调用 flod 的目标仅仅是定义了 Lambda 表达式,然而此处并没有调用这个表达式。我过后也被这个高阶函数搞懵一小下下。// 已知 co1 是 Element 类型,看下 Element::fold 的实现,恍然大悟了,持续
            
            context.fold(this) { acc, element ->
                //acc 是 co1,element 元素是[传入的 context] co2
                // 看看 Element 的 minusKey,key 相等,返回了 EmptyCoroutineContext,这里就意味着把 co1 给删了,因为他们相等啊。val removed = acc.minusKey(element.key)
                //EmptyCoroutineContext 返回时示意 key 雷同,间接把新 element 返回了。即当初的 context 就等于 CoroutineName("Co-02")。//【K1】流程剖析到此结束。持续拐回去,剖析一下如果持续追加上下文,CoroutineName("Co-02") + DispatcherContext。会产生什么事件。if (removed === EmptyCoroutineContext) element else {val interceptor = removed[ContinuationInterceptor]
                    if (interceptor == null) CombinedContext(removed, element) else {val left = removed.minusKey(ContinuationInterceptor)
                        if (left === EmptyCoroutineContext) CombinedContext(element, interceptor) else
                            CombinedContext(CombinedContext(left, element), interceptor)
                    }
                }
            }

    /**
     * 返回一个去除链表中 key 的对象的上下文。*/
    public fun minusKey(key: Key<*>): CoroutineContext

    /**
     *  Key 标识接口
     */
    public interface Key<E : Element>

    /**
     * 上下文链表中元素标识接口
     */
    public interface Element : CoroutineContext {
        /**
         * 这个元素对应的键
         */
        public val key: Key<*>

        /**
         * 
         */
        public override operator fun <E : Element> get(key: Key<E>): E? =
            @Suppress("UNCHECKED_CAST")
            if (this.key == key) this as E else null

        /**
         * 进行累加的高阶函数。因为 element 对象不是一个汇合对象,所以这里不会递归,只会执行一次 operation
         */
        public override fun <R> fold(initial: R, operation: (R, Element) -> R): R =
            operation(initial, this)

        /**
         * 如果以后 key 相等那么返回 EmptyCoroutineContext。这个函数寓意就是用来去除 key
         */
        public override fun minusKey(key: Key<*>): CoroutineContext =
            if (this.key == key) EmptyCoroutineContext else this
    }
}

在剖析一个状况:CoroutineName("Co-02") + DispatcherContext

此次剖析代号K2.

案例 K2

 public operator fun plus(context: CoroutineContext): CoroutineContext =
        if (context === EmptyCoroutineContext) this else // fast path -- avoid lambda creation
           
            // 已知 this 为 co2,是 Element 类型
            context.fold(this) { acc, element ->
                //acc 是 co2,element 元素是 [传入的拦截器] 缩写为:DP
                // 看看 Element 的 minusKey,key 不相等,removed=co2
                val removed = acc.minusKey(element.key)
                if (removed === EmptyCoroutineContext) element else {// 进到这里。// 已知 removed 代表老列表,即 co2.
                    // 这里做的事件目标:因为拦截器开发中应用频繁,所以这里是做优化,把拦截器放在链表开端,减少获取拦截器时的效率
                    // 查看 Element::get 可知 key 不雷同,remove 是 Name 而 element 是 ContinuationInterceptor,这里返回 null
                    val interceptor = removed[ContinuationInterceptor]
                    // 如果拦截器对象为空的话,间接把 Dp(拦截器)拼接 CombinedContext 对象中。这时候人造在队尾。【K2】剖析实现。if (interceptor == null) CombinedContext(removed, element) else {val left = removed.minusKey(ContinuationInterceptor)
                        if (left === EmptyCoroutineContext) CombinedContext(element, interceptor) else
                            CombinedContext(CombinedContext(left, element), interceptor)
                    }
                }
            }

持续:

CoroutineName("Co-02") + DispatcherContext + ExceptionHandlerContext

此次剖析代号K3.

案例 K3

 public operator fun plus(context: CoroutineContext): CoroutineContext =
        if (context === EmptyCoroutineContext) this else // fast path -- avoid lambda creation
           
            // 已知 this 为 CombinedContext(left:co2,element:disp),context 为 ExceptionHandlerContext,Elemnet 类型
            context.fold(this) { acc, element ->
                //acc 是 CombinedContext(co2,disp),element 元素是[传入的 context] Ex
                // 看看 CombinedContext 的 minusKey,key 不相等,removed=co2
                val removed = acc.minusKey(element.key)// 能够去看 CombinedContext::minusKey, 做了简略正文
                if (removed === EmptyCoroutineContext) element else {// 没有命中,进到这里。// 已知 removed 代表老列表,即 CombinedContext(left:co2,element:disp).
                    // 这里做的事件目标:因为拦截器开发中应用频繁,所以这里是做优化,把拦截器放在链表开端,减少获取拦截器时的效率
                    // 查看 CombinedContext::get , 找到拦截器 dp,val interceptor = removed[ContinuationInterceptor]
                    // 如果没有拦截器,间接加到队尾就行了。因为这样不影响拦截器在队尾的准则
                    if (interceptor == null) CombinedContext(removed, element) else {
                        // 咱们之前设置 dp,所以不为空。查看 CombinedContext::minusKey,命中了 dp,把除了 dp 其余的局部当作 left 返回了。val left = removed.minusKey(ContinuationInterceptor)
                        // 如果新 left 为空,那么就没必要保留他了。间接让它隐没。if (left === EmptyCoroutineContext) CombinedContext(element, interceptor) else
                            // 拦截器放链表最初
                            CombinedContext(CombinedContext(left, element), interceptor)
                    }
                }
            }

不晓得你们感触如何,反正我是要写到吐了。递归代码就是这样不易浏览。只能通过逐句解析,让本人尽量放弃相熟水平。

再来一个最色的剖析 —–K4,当作本篇的一个终结

案例 K4

val context1:CoroutineContext = CoroutineName("Co-01");
val context2:CoroutineContext = DispatcherContext + ExceptionHandlerContext;
....
override val context: CoroutineContext = context1 + context2;

这样之前的的剖析逻辑就全变了。context1 是 element,context2 是 CombinedContext

 public operator fun plus(context: CoroutineContext): CoroutineContext =
        if (context === EmptyCoroutineContext) this else // fast path -- avoid lambda creation
           
            // 已知 this 为 CoroutineName(Element),context 为 CombinedContext(left: dp, element: Ex)
            context.fold(this) { acc, element -> //fold 不再是 Element::fold 而是 CombinedContext::fold。//acc 是 left.fold(initial, operation), 又是递归,fk
               /* 即:dp.fold(acc:CoroutineName,operation)。这又回到了 Element::fold ==> dp.fold 中 acc:CoroutineName,element this 即 dp                   */
                //acc:CoroutineName 不包含 dp,所以 remove 等于 acc:CoroutineName                  
                val removed = acc.minusKey(element.key)
                if (removed === EmptyCoroutineContext) element else {// 没有命中,进到这里。// 已知 removed 代表老列表,即 acc:CoroutineName
                    val interceptor = removed[ContinuationInterceptor]
                    // 如果没有拦截器,间接加到队尾 CombinedContext(CoroutineName, dp) dp.fold 返回了 CombinedContext(CoroutineName, dp)
                    if (interceptor == null) CombinedContext(removed, element) else {val left = removed.minusKey(ContinuationInterceptor)
                        if (left === EmptyCoroutineContext) CombinedContext(element, interceptor) else
                            // 拦截器放链表最初
                            CombinedContext(CombinedContext(left, element), interceptor)
                    }
                }
            }
//=============================CombinedContext::fold===========================================
 public override fun <R> fold(initial: R, operation: (R, Element) -> R): R =
        operation(left.fold(initial, operation), element)
//=============================Element::fold========================
    public override fun <R> fold(initial: R, operation: (R, Element) -> R): R =
            operation(initial, this)

持续往下:


//=============================CombinedContext::fold===========================================
 public override fun <R> fold(initial: R, operation: (R, Element) -> R): R =
        operation(CombinedContext(CoroutineName, dp), element)//element 是 Ex。
// 回到下层递归
{ acc, element ->
                //acc 是 CombinedContext(CoroutineName, dp),element:ex
                //acc 不包含 ex,所以 remove 等于 CombinedContext(CoroutineName, dp)
                val removed = acc.minusKey(element.key)
                if (removed === EmptyCoroutineContext) element else {// 进到这里。// 已知 removed 代表老列表,即 CombinedContext(CoroutineName, dp)
                    val interceptor = removed[ContinuationInterceptor]// 取出 dp 拦截器这一项
                    if (interceptor == null) CombinedContext(removed, element) else {
                        // 进到这里
                        val left = removed.minusKey(ContinuationInterceptor)//CoroutineName
                        if (left === EmptyCoroutineContext) CombinedContext(element, interceptor) else
                            // 最初的返回值。left :CoroutineName,element : ex , interceptor: dp.【K4】剖析完结
                            CombinedContext(CombinedContext(left, element), interceptor)
                    }
                }

论断

1.CoroutineName("Co-02") + EmptyCoroutineContext 后果:CoroutineName("Co-02")

2.CoroutineName("Co-01") + CoroutineName("Co-02") = CoroutineName("Co-02")主动去重,新的替换老的

3.CoroutineName("Co-01") + Dispatchers.Main + ExceptionHandlerCoroutineContext = CoroutineName("Co-01") <- ExceptionHandlerCoroutineContext <- Dispatchers.Main (拦截器 ContinuationInterceptor 肯定在链表尾部(最右侧),这样的益处是 get 间接拿不必向左遍历)

当 context+element 的时候

  • 如果 context 等于 empty,那么间接返回 element
  • 如果 context 含有与 element 的 key 雷同的元素,那么 context 删除老元素,增加新元素。
  • 每增加一个新元素后,会检测链表中是否有“拦截器”如果有,则必须把拦截器放在队尾,升高 get 老本
  • 当 element 为 Element 的类型时

    • context + CoroutineName = CombinedContext(left: context,element: CoroutineName);
  • 当 element 为 CombinedContext 时

    • (context + CombinedContext(A,B,C)= CombinedContext(left:CombinedContext(left:CombinedContext(left: context,element: A),element: B),element: C);
    • 下面的式子只是想阐明,[addAll]操作并不是简略的通过更改链表指针的形式实现,取出指标列表中每一个 element,创立新的 CombinedContext 键入到原来的上下文表中。从头到尾通过递归,全部都是新创建的节点。
正文完
 0