一、前言
在前面已经了解了协程的使用方法,为了能够更深入的了解协程的设计原理,那么我们来探究以下底层的实现原理。
二、执行过程分析
我们了解到,协程的创建和执行需要在CoroutineScope中,下面我们借助源码来分析CoroutineScope的执行过程。
2.1 创建CoroutineScope对象
val scope = CoroutineScope(Job()+Dispatchers.IO)
CoroutineScope的创建很简单,传入一个CoroutineContext。
下面我们看一下CoroutineScope的代码:
/**
* Creates a [CoroutineScope] that wraps the given coroutine [context].
*
* If the given [context] does not contain a [Job] element, then a default `Job()` is created.
* This way, cancellation or failure of any child coroutine in this scope cancels all the other children,
* just like inside [coroutineScope] block.
*/
public fun CoroutineScope(context: CoroutineContext): CoroutineScope =
ContextScope(if (context[Job] != null) context else context + Job())
CoroutineScope返回一个根据我们传入的CoroutineContext进行包装,如果传入的context没有Job,那么会使用默认的Job(),CoroutineScope()函数返回ContextScope,ContextScope的代码比较简单,实现CoroutineScope接口,重写coroutineContext属性,并赋值。
internal class ContextScope(context: CoroutineContext) : CoroutineScope {
override val coroutineContext: CoroutineContext = context
// CoroutineScope is used intentionally for user-friendly representation
override fun toString(): String = "CoroutineScope(coroutineContext=$coroutineContext)"
}
ContextScope的创建过程中,我们看到一个特殊的操作操作符“+”,这个对应的是CoroutineContext中的plus方法
public interface CoroutineContext {
public operator fun plus(context: CoroutineContext): CoroutineContext =
if (context === EmptyCoroutineContext) this else // fast path -- avoid lambda creation
context.fold(this) { acc, element ->
val removed = acc.minusKey(element.key)
if (removed === EmptyCoroutineContext) element else {
// make sure interceptor is always last in the context (and thus is fast to get when present)
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)
}
}
}
}
这里关于plus的具体实现可以参考源码中各个CoroutineContext子类的实现,下面用一张图来表示plus()函数最终的效果:
这里有个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"
}
这里的EmptyCoroutineContext定义为了一个单例,实现CoroutineContext中相关的方法。
2.2 scope.launch()的执行过程分析
CoroutineScope创建完成后,需要通过launch函数来执行协程代码,下面分析launch函数的执行过程。
scope.launch {
print("this code execute in coroutine")
}
launch后面的lambda就是我们协程,也是我们业务代码执行的代码块,下面看一下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
}
launch()是CoroutineScope的扩展方法,三个参数中,CoroutineContext和CoroutineStart都提供了默认的值,该方法最终返回一个Job。该方法首先通过newCoroutineContext()函数返回一个CoroutineContext,下面看一下newCoroutineContext()函数:
public actual fun CoroutineScope.newCoroutineContext(context: CoroutineContext): CoroutineContext {
val combined = coroutineContext + context
val debug = if (DEBUG) combined + CoroutineId(COROUTINE_ID.incrementAndGet()) else combined
return if (combined !== Dispatchers.Default && combined[ContinuationInterceptor] == null)
debug + Dispatchers.Default else debug
}
这个函数没有太多的逻辑,控制debug。CoroutineStart没有指定的情况下,默认的是CoroutineStart.DEFAULT,因此会进入coroutine = StandaloneCoroutine,接下来执行coroutine.start(start, coroutine, block),
/**
* Starts this coroutine with the given code [block] and [start] strategy.
* This function shall be invoked at most once on this coroutine.
*
* First, this function initializes parent job from the `parentContext` of this coroutine that was passed to it
* during construction. Second, it starts the coroutine based on [start] parameter:
*
* * [DEFAULT] uses [startCoroutineCancellable].
* * [ATOMIC] uses [startCoroutine].
* * [UNDISPATCHED] uses [startCoroutineUndispatched].
* * [LAZY] does nothing.
*/
public fun <R> start(start: CoroutineStart, receiver: R, block: suspend R.() -> T) {
initParentJob()
start(block, receiver, this)
}
接下来进入CoroutineStart的invoke()方法
public enum class CoroutineStart {
......
public operator fun <R, T> invoke(block: suspend R.() -> T, receiver: R, completion: Continuation<T>): Unit =
when (this) {
DEFAULT -> block.startCoroutineCancellable(receiver, completion)
ATOMIC -> block.startCoroutine(receiver, completion)
UNDISPATCHED -> block.startCoroutineUndispatched(receiver, completion)
LAZY -> Unit // will start lazily
}
}
在launch()方法中,CoroutineStart默认是DEFAULT,接下来执行startCoroutineCancellable()
internal fun <R, T> (suspend (R) -> T).startCoroutineCancellable(
receiver: R, completion: Continuation<T>,
onCancellation: ((cause: Throwable) -> Unit)? = null
) =
runSafely(completion) {
createCoroutineUnintercepted(receiver, completion).intercepted().resumeCancellableWith(Result.success(Unit), onCancellation)
}
private inline fun runSafely(completion: Continuation<*>, block: () -> Unit) {
try {
block()
} catch (e: Throwable) {
completion.resumeWith(Result.failure(e))
}
}
最后
协程的内容很多,其中的suspend是很重要的一块,这块的实现逻辑还没有完全搞清楚,后期再来弥补这些内容。