上下文:是指执行环境相关的通用数据资源的统一提供者。


  • 协程的上下文数据结构特征实现与List、Map集合非常类似,协程的上下文作为一个集合,其元素类型是Element
  • 协程上下文的内部实现实际上是一个单链表。

Element

An element of the CoroutineContext. An element of the coroutine context is a singleton context by itself.

    public interface Element : CoroutineContext {
        /**
         * A key of this coroutine context element.
         */
        public val key: Key<*>

        //....
    }
  • Element接口中有一个属性key,其作用就是协程上下文这个集合中元素的"索引",类似集合元素的下标,但和集合不同的是该“索引”是在数据里面的。

协程上下文元素的实现


除了Element接口外,还有一个AbstractCoroutineContextElement的抽象类,能更加方便实现协程上下文的元素。

public abstract class AbstractCoroutineContextElement(public override val key: Key<*>) : Element

协程名的实现

/**
 * CoroutineNameElement允许我们为协程绑定一个名字
 */
class CoroutineNameElement(name: String) : AbstractCoroutineContextElement(KEY){
    companion object KEY : CoroutineContext.Key<CoroutineNameElement>
}

协程异常处理器的实现

/**
 * CoroutineExceptionHandlerElement允许我们在启动协程的时候安装一个统一的异常处理器
 */
class CoroutineExceptionHandlerElement(val onErrorAction: (Throwable) -> Unit) : AbstractCoroutineContextElement(KEY) {
    companion object KEY : CoroutineContext.Key<CoroutineExceptionHandlerElement>

    fun onError(error: Throwable) {
        error.printStackTrace()
        onErrorAction(error)
    }
}

协程上下文的使用

fun main() {
    var coroutineContext: CoroutineContext = EmptyCoroutineContext
    //类似集合一样有两种添加上下文的方式
    //1️⃣
    //coroutineContext += CoroutineNameElement("co-01")
    //coroutineContext += CoroutineExceptionHandlerElement {
    //    println("error::::${it}")
    //}
    //2️⃣
    coroutineContext += CoroutineNameElement("co-01") + CoroutineExceptionHandlerElement {
        println("error::::${it}")
    }
    suspend {
        //在协程内部获取上下文
        val nameElement: CoroutineNameElement? = coroutineContext[CoroutineNameElement]
        println("In Coroutine [${nameElement?.name}].")
        100 / 0
    }.startCoroutine(object : Continuation<Int> {
        override val context: CoroutineContext
            get() = coroutineContext

        override fun resumeWith(result: Result<Int>) {
            result.onFailure {
                context[CoroutineExceptionHandlerElement]?.onError(it)
            }
        }
    })
}
  • 通过resumeWith里的context.get()或者协程体里的coroutineContext.get()获取协程上下文元素。因为有可能没设置CoroutineExceptionHandlerElement、CoroutineNameElement…所以get()返回的结果是可空类型。
  • context.get(key)中key是协程上下文的key。