挖坑kotlin協程,預計分多篇文章徹底梳理一遍kotlin協程框架,廢話不多說,先從協程作用域開始。
協程作用域CoroutinScope
在了解協程上下文之前,先要談談協程作用域-CoroutinScope,協程作用域可以理解為你創建的協程的約束范圍,協程是運行在你約束的一個范圍內的,這樣就劃分了協程的運行范圍,對于協程的生命周期管理更加規范。每個協程構建器(如啟動、異步等)都是 CoroutineScope 上的擴展,并繼承其 coroutineContext 以自動傳播其所有元素和取消。
CoroutinScope是一個接口,源碼定義如下:
public interface CoroutineScope {
// 協程上下文,用來管理協程里的上下文元素
public val coroutineContext: CoroutineContext
}
看看協程框架里提供的全局作用域GlobalScope:
// 注意看它用object修飾了,代表他的生命周期是跟進程綁定的,而且是單例
public object GlobalScope : CoroutineScope {
/**
* 一個空的協程上下文
*/
override val coroutineContext: CoroutineContext
get() = EmptyCoroutineContext
}
也可以自己根據需求創建作用域,官方推薦使用CoroutineScope(context: CoroutineContext)或者MainScope():
// 創建一個運行在主線程的作用域
public fun MainScope(): CoroutineScope = ContextScope(SupervisorJob() + Dispatchers.Main)
// 自定義協程上下文的作用域創建方式
public fun CoroutineScope(context: CoroutineContext): CoroutineScope =
ContextScope(if (context[Job] != null) context else context + Job())
協程上下文CoroutineContext
協程上下文從字面意思來看,就是貫穿你創建的某個協程的一個管家,可以結合Android開發中的上下文來理解,它持有協程運行時的各個參數。因為剛開始學習協程,這里先給個模糊描述,否則一開始給出一堆陌生艱澀的術語強行讓你理解,你也會一頭霧水,隨著對協程框架的深入,自然會漸漸領會。
接下來就詳細看看CoroutinContext,它也是一個接口:
public interface CoroutineContext {
// 由operator修飾的操作符重載,對應“[]”操作符
// 通過key獲取一個Element對象
public operator fun <E : Element> get(key: Key<E>): E?
// 折疊方法,提供一個初始值R和操作lambda,該方法被其子類Element實現
public fun <R> fold(initial: R, operation: (R, Element) -> R): R
// 由operator修飾的操作符重載,對應“+”操作符;
// 合并兩個CoroutineContext對象中的Element元素,將合并后的上下文返回,如果存在相同key的Element對象,則對其進行覆蓋;
// EmptyCoroutineContext一個空實現的上下文;
// CombinedContext是CoroutineContext接口的一個實現類,也是鏈表的具體實現的一個節點,節點存在兩個元素:element 當前的節點的集合元素,left CoroutineContext類型,指向鏈表的下一個元素;
// 另外plus函數在合并上下文的過程中將Key為ContinuationInterceptor的元素保持在鏈表的尾部,方便其快速的讀取;
// 先了解ContinuationInterceptor是一個攔截器,下文中會介紹它
public operator fun plus(context: CoroutineContext): CoroutineContext =
if (context === EmptyCoroutineContext) this else // 如果待合并的context是一個空上下文,返回當前的上下文
// fold遍歷context集合
context.fold(this) { acc, element ->//acc為當前上下文的集合,element為context集合的元素
val removed = acc.minusKey(element.key)//移除aac集合中的element元素,并返回移除后的一個集合
if (removed === EmptyCoroutineContext)
element // 如果移除后集合是一個空的上下文集合,那么當前element元素為合并后的上下文集合
else {
val interceptor = removed[ContinuationInterceptor]//獲取攔截器
if (interceptor == null) CombinedContext(removed, element) // 如果interceptor為空,生成CombinedContext節點,CombinedContext元素為element,指向的鏈表節點是removed
else {
// 將攔截器移至鏈表尾部方便讀取
val left = removed.minusKey(ContinuationInterceptor)
if (left === EmptyCoroutineContext) CombinedContext(element, interceptor) else
CombinedContext(CombinedContext(left, element), interceptor)
}
}
}
// 刪除對應key的Element元素,返回刪除后CoroutineContext
public fun minusKey(key: Key<*>): CoroutineContext
// 集合中每個元素的key
public interface Key<E : Element>
// 集合中的元素定義,也是一個接口
public interface Element : CoroutineContext {
// 元素的key
public val key: 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)
// 刪除對應key的Element元素
public override fun minusKey(key: Key<*>): CoroutineContext =
if (this.key == key) EmptyCoroutineContext else this
}
}
上面源碼里提到了一個很重要的類CombinedContext,這個類很重要,對于協程上下文的數據結構存儲很重要:
// 左向鏈表實現
// element集合元素
// left 鏈表的下一個節點
internal class CombinedContext(
private val left: CoroutineContext,
private val element: Element
) : CoroutineContext, Serializable {
// 在集合中獲取一個以key為鍵的元素
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)
// 在集合中刪除一個鍵值為key的元素
public override fun minusKey(key: Key<*>): CoroutineContext {
element[key]?.let { return left }
val newLeft = left.minusKey(key)
return when {
newLeft === left -> this
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
...
}
從以上源碼可以猜得出來,協程上下文是有個集合來管理上下文元素Element的,而這個Element的集合就是協程真正的上下文,里面包含被封裝成Element的各個運行要素。它的結構設計使得很容易把他們拼裝和獲取。舉個例子:
// 上下文組合添加
var context = CoroutineName("MainActivity") + Dispatchers.Main
println("1context:$context")
// 添加重復類型上下文會覆蓋之前的
context += Dispatchers.Default
println("2context:$context")
// 獲取CoroutineName,注意使用的key
println("3context:${context[CoroutineName.Key]}")
// 移除CoroutineName
context = context.minusKey(CoroutineName.Key)
println("4context:$context")
輸出結果:
1context:[CoroutineName(MainActivity), Dispatchers.Main[missing, cause=java.lang.RuntimeException: Stub!]]
2context:[CoroutineName(MainActivity), Dispatchers.Default]
3context:CoroutineName(MainActivity)
4context:Dispatchers.Default
以上的CoroutineName和Dispatchers.main都屬于Element的實現類,也是協程中上下文元素,類似的上下文元素還有很多,比如Job、CoroutineId等等。通過以上的方法可以很好管理這些元素。
協程啟動策略-CoroutineStart
定義協程生成器的啟動選項。定義在一個枚舉類里面:
public enum class CoroutineStart {
DEFAULT,
LAZY,
@ExperimentalCoroutinesApi // Since 1.0.0, no ETA on stability
ATOMIC,
UNDISPATCHED;
@InternalCoroutinesApi
public operator fun <T> invoke(block: suspend () -> T, completion: Continuation<T>): Unit =
when (this) {
DEFAULT -> block.startCoroutineCancellable(completion)
ATOMIC -> block.startCoroutine(completion)
UNDISPATCHED -> block.startCoroutineUndispatched(completion)
LAZY -> Unit // will start lazily
}
@InternalCoroutinesApi
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
}
/**
* Returns `true` when [LAZY].
*
* @suppress **This an internal API and should not be used from general code.**
*/
@InternalCoroutinesApi
public val isLazy: Boolean get() = this === LAZY
}
DEFAULT -- 立即根據其上下文安排協程執行;
LAZY -- 僅在需要時懶惰地啟動協程;
ATOMIC -- 原子(以不可取消的方式)根據其上下文安排執行協程;
UNDISPATCHED -- 立即執行協程,直到當前線程中的第一個掛起點。
這里暫時不展開解釋每個啟動方式的含義,先有個基本印象即可。
launch()方法啟動協程
啟動協程最常用的一個方式,launch()方法是協程框架提供的CoroutineScope的擴展方法,意味著必須在協程作用域里面執行:
public fun CoroutineScope.launch(
context: CoroutineContext = EmptyCoroutineContext, // 上下文
start: CoroutineStart = CoroutineStart.DEFAULT, // 啟動策略
block: suspend CoroutineScope.() -> Unit // 協程代碼塊
): Job {
// 第一步,先把傳進來的上下文組裝成一個新的
val newContext = newCoroutineContext(context)
// 第二步,使用組合后的newContext構建一個Coroutine,分析的時候我們使用DEFAULT策略,所以這里創建的是StandaloneCoroutine,
// 它實際是個實現了續體Continuation和CoroutineScope和Job的子類,具體后面分析
val coroutine = if (start.isLazy)
LazyStandaloneCoroutine(newContext, block) else
StandaloneCoroutine(newContext, active = true)
// 第三步,啟動協程
coroutine.start(start, coroutine, block)
// 第四步, 作為Job返回這個實例,job可以用來控制協程的一些運行狀態,可以看做是協程體的引用
return coroutine
}
// newCoroutineContext是一個擴展函數
public actual fun CoroutineScope.newCoroutineContext(context: CoroutineContext): CoroutineContext {
// 符號“+”對應CoroutineContext的plus方法
val combined = coroutineContext + context
// 看下else非debug的情況,得到合并后的combined復制給變量debug
val debug = if (DEBUG) combined + CoroutineId(COROUTINE_ID.incrementAndGet()) else combined
// 實例中調度器使用的Dispatchers.Default,所以這里執行else分支,直接返回coroutineContext + context相加后的結果
return if (combined !== Dispatchers.Default && combined[ContinuationInterceptor] == null)
debug + Dispatchers.Default else debug
}
現在我們在MainActivity啟動一個協程,一步步看看它是怎么運行的:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
GlobalScope.launch(Dispatchers.Default){
println("launch test")
}
}
}
很簡單的協程,沒有掛起函數,只是在協程里執行了一個println()函數,接下來就跟著launch()方法一步步走下去看看協程是怎么運行起來的。
協程調度器Dispatchers
注意看,launch()方法里我們傳入了Dispatchers.Default,這個對應的是參數context:CoroutinContext,說明它實現了CoroutineContext接口,結合之前分析的launch方法代碼,這個調度器被組合進了newContext傳入到StandaloneCoroutine里去了。來看看Dispatchers.Default是個啥:
/**
* Groups various implementations of [CoroutineDispatcher].
*/
public actual object Dispatchers {
@JvmStatic
public actual val Default: CoroutineDispatcher = DefaultScheduler
@JvmStatic
public actual val Main: MainCoroutineDispatcher get() = MainDispatcherLoader.dispatcher
@JvmStatic
public val IO: CoroutineDispatcher = DefaultIoScheduler
@JvmStatic
public actual val Unconfined: CoroutineDispatcher = kotlinx.coroutines.Unconfined
@DelicateCoroutinesApi
public fun shutdown() {
DefaultExecutor.shutdown()
// Also shuts down Dispatchers.IO
DefaultScheduler.shutdown()
}
}
可以看到Dispatchers.Default是對象聲明Dispatchers的不可變量,類型是CoroutineDispatcher。
注意它是全局單例存在的,代表其他協程使用它的時候,用的都是同一個實例,這里可以提前說明一下Dispatchers.Default維護了一個線程池,所以全局的協程(使用Dispatchers.Default或Dispatchers.IO作為上下文)都是共用一個線程池。這個特性要記住!!
繼續看看Dispatchers.Default的實現:
// Instance of Dispatchers.Default 依然是個單例對象!
internal object DefaultScheduler : SchedulerCoroutineDispatcher(
CORE_POOL_SIZE, MAX_POOL_SIZE,
IDLE_WORKER_KEEP_ALIVE_NS, DEFAULT_SCHEDULER_NAME
) {
// Shuts down the dispatcher, used only by Dispatchers.shutdown()
internal fun shutdown() {
super.close()
}
// Overridden in case anyone writes (Dispatchers.Default as ExecutorCoroutineDispatcher).close()
override fun close() {
throw UnsupportedOperationException("Dispatchers.Default cannot be closed")
}
override fun toString(): String = "Dispatchers.Default"
}
沒什么可研究的信息,繼續看他的父類SchedulerCoroutineDispatcher,看到它帶著Scheduler字樣的名字和CORE_POOL_SIZE, MAX_POOL_SIZE這些構造參數,可以判斷它跟線程池有關。
// Instantiated in tests so we can test it in isolation
internal open class SchedulerCoroutineDispatcher(
private val corePoolSize: Int = CORE_POOL_SIZE,
private val maxPoolSize: Int = MAX_POOL_SIZE,
private val idleWorkerKeepAliveNs: Long = IDLE_WORKER_KEEP_ALIVE_NS,
private val schedulerName: String = "CoroutineScheduler",
) : ExecutorCoroutineDispatcher() {
// Executor就是java里的線程池頂級接口
override val executor: Executor
get() = coroutineScheduler
// 協程的線程池實例
private var coroutineScheduler = createScheduler()
// 協程的線程池類CoroutineScheduler,這里暫時不深究這個類的實現,把它理解成java里的線程池一樣的性質就行
private fun createScheduler() =
CoroutineScheduler(corePoolSize, maxPoolSize, idleWorkerKeepAliveNs, schedulerName)
// 協程的調度方法,最后還是交給了線程池去調度,所謂調度,看來可以理解成線程切換
override fun dispatch(context: CoroutineContext, block: Runnable): Unit = coroutineScheduler.dispatch(block)
override fun dispatchYield(context: CoroutineContext, block: Runnable): Unit =
coroutineScheduler.dispatch(block, tailDispatch = true)
internal fun dispatchWithContext(block: Runnable, context: TaskContext, tailDispatch: Boolean) {
coroutineScheduler.dispatch(block, context, tailDispatch)
}
override fun close() {
coroutineScheduler.close()
}
...略
}
從SchedulerCoroutineDispatcher的源碼可以看出,這個類維護了一個線程池,用來執行協程線程的切換。再看看它的父類ExecutorCoroutineDispatcher:
public abstract class ExecutorCoroutineDispatcher: CoroutineDispatcher(), Closeable {
/** @suppress */
@ExperimentalStdlibApi
public companion object Key : AbstractCoroutineContextKey<CoroutineDispatcher, ExecutorCoroutineDispatcher>(
CoroutineDispatcher,
{ it as? ExecutorCoroutineDispatcher })
/**
* Underlying executor of current [CoroutineDispatcher].
*/
public abstract val executor: Executor
/**
* Closes this coroutine dispatcher and shuts down its executor.
*
* It may throw an exception if this dispatcher is global and cannot be closed.
*/
public abstract override fun close()
}
是個抽象類,只是定義了協程調度器的規范,繼續看它的父類CoroutineDispatcher:
public abstract class CoroutineDispatcher :
AbstractCoroutineContextElement(ContinuationInterceptor), ContinuationInterceptor {
/** @suppress */
@ExperimentalStdlibApi
public companion object Key : AbstractCoroutineContextKey<ContinuationInterceptor, CoroutineDispatcher>(
ContinuationInterceptor,
{ it as? CoroutineDispatcher })
public open fun isDispatchNeeded(context: CoroutineContext): Boolean = true
// 調度方法是在這里定義的
public abstract fun dispatch(context: CoroutineContext, block: Runnable)
@InternalCoroutinesApi
public open fun dispatchYield(context: CoroutineContext, block: Runnable): Unit = dispatch(context, block)
// 核心方法interceptContinuation,攔截續體,包裝成了成了DispatchedContinuation,DispatchedContinuation也是一個很重要的類
// 協程的攔截器實現
public final override fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T> =
DispatchedContinuation(this, continuation)
...略
}
// 續體攔截器接口
public interface ContinuationInterceptor : CoroutineContext.Element {
/**
* The key that defines *the* context interceptor.
*/
companion object Key : CoroutineContext.Key<ContinuationInterceptor>
public fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T>
...略
}
通過以上源碼分析,Dispatchers.Default也就是協程調度器CoroutineDispatcher主要有兩個核心方法,
dispatch(context: CoroutineContext, block: Runnable):用來切換線程
interceptContinuation(continuation: Continuation<T>): Continuation<T>:用來攔截續體Continuation,將其包裝成DispatchedContinuation
記住這上面兩個方法,后面的流程分析會多次用到。
續體Continuation
在上面的代碼分析中頻繁出現了一個概念-續體Continuation,這個概念非常重要,協程啟動,掛起,恢復都有它有關,看看它的定義,是一個接口:
public interface Continuation<in T> {
/**
* 持有協程上下文
*/
public val context: CoroutineContext
/**
* 核心方法,恢復、開啟協程體的執行入口方法
*
*/
public fun resumeWith(result: Result<T>)
}
要理解續體是怎么工作的,我們繼續跟著launch函數走:
public fun CoroutineScope.launch(
context: CoroutineContext = EmptyCoroutineContext, // 上下文
start: CoroutineStart = CoroutineStart.DEFAULT, // 啟動策略
block: suspend CoroutineScope.() -> Unit // 協程代碼塊
): Job {
// 第一步,先把傳進來的上下文組裝成一個新的
val newContext = newCoroutineContext(context)
// 第二步,構建一個Coroutine,分析的時候我們使用DEFAULT策略,所以這里創建的是StandaloneCoroutine,
// 它實際是個實現了續體Continuation和CoroutineScope和Job的子類,具體后面分析
val coroutine = if (start.isLazy)
LazyStandaloneCoroutine(newContext, block) else
StandaloneCoroutine(newContext, active = true)
// 第三步,啟動協程
coroutine.start(start, coroutine, block)
// 第四步, 作為Job返回這個實例,job可以用來控制協程的一些運行狀態,可以看做是協程體的引用
return coroutine
}
第三步調用了StandaloneCoroutine的start方法:
public fun <R> start(start: CoroutineStart, receiver: R, block: suspend R.() -> T) {
start(block, receiver, this)
}
接著調用了CoroutineStart的invoke方法(kotlin里invoke方法可以通過對象來調用,這里官方實在很惡心,到處埋雷):
public enum class CoroutineStart {
// 到這里理一下對應關系,入參receiver和completion都是launch方法里StandaloneCoroutine實例coroutine,R泛型這里是協程作用域CoroutineScope
// StandaloneCoroutine也是實現了CoroutineScope接口的,completion則是續體Continuation,StandaloneCoroutine也實現了Continuation
// block是我們業務代碼傳入的協程代碼塊
public operator fun <R, T> invoke(block: suspend R.() -> T, receiver: R, completion: Continuation<T>): Unit =
when (this) {
// 我們使用的DEFAULT策略,執行這里啟動
DEFAULT -> block.startCoroutineCancellable(receiver, completion)
ATOMIC -> block.startCoroutine(receiver, completion)
UNDISPATCHED -> block.startCoroutineUndispatched(receiver, completion)
LAZY -> Unit // will start lazily
}
... 略
}
// StandaloneCoroutine,其父類實現了Coroutine和CoroutineScope
private open class StandaloneCoroutine(
parentContext: CoroutineContext,
active: Boolean
) : AbstractCoroutine<Unit>(parentContext, initParentJob = true, active = active) {
override fun handleJobException(exception: Throwable): Boolean {
handleCoroutineException(context, exception)
return true
}
}
// 重點看父類AbstractCoroutine,注意它的構造參數parentContext,傳入了父協程上下文
public abstract class AbstractCoroutine<in T>(
parentContext: CoroutineContext,
initParentJob: Boolean,
active: Boolean
) : JobSupport(active), Job, Continuation<T>, CoroutineScope {
/**
* 合并了上下文元素
*/
@Suppress("LeakingThis")
public final override val context: CoroutineContext = parentContext + this
/**
* 關注一下這個方法,續體里定義的抽象方法
*/
public final override fun resumeWith(result: Result<T>) {
val state = makeCompletingOnce(result.toState())
if (state === COMPLETING_WAITING_CHILDREN) return
afterResume(state)
}
protected open fun afterResume(state: Any?): Unit = afterCompletion(state)
public fun <R> start(start: CoroutineStart, receiver: R, block: suspend R.() -> T) {
start(block, receiver, this)
}
... 略
}
繼續跟蹤block.startCoroutineCancellable(receiver, completion),block是我們啟動協程傳入的lambda,startCoroutineCancellable是lambda函數的擴展函數:
public fun <T> (suspend () -> T).startCoroutineCancellable(completion: Continuation<T>): Unit = runSafely(completion) {
createCoroutineUnintercepted(completion).intercepted().resumeCancellableWith(Result.success(Unit))
}
繼續看方法體中第一個方法createCoroutineUnintercepted,這個方法也是suspend () -> T 函數的擴展函數,它是這樣的:
public actual fun <T> (suspend () -> T).createCoroutineUnintercepted(
completion: Continuation<T>
): Continuation<Unit> {
// 這里對completion進行了一層封裝
val probeCompletion = probeCoroutineCreated(completion)
// 判斷函數suspend () -> T 實例是否屬于BaseContinuationImpl的子類
return if (this is BaseContinuationImpl)
// 是的話就調用其create方法
// 這里涉及到kotlin編譯的問題,用kotlin看源碼往往很蛋疼,這時候就需要把之前的那個協程體反編譯成java來看看
create(probeCompletion)
else
createCoroutineFromSuspendFunction(probeCompletion) {
(this as Function1<Continuation<T>, Any?>).invoke(it)
}
}
// MainActivity里協程啟動的方法反編譯成java代碼
BuildersKt.launch$default((CoroutineScope)((CoroutineScope)GlobalScope.INSTANCE)
, (CoroutineContext)((CoroutineContext)Dispatchers.getDefault())
, null
// 這是我們寫的lambda代碼塊,被包裝成了Function2<CoroutineScope, Continuation<? super Unit>, Object>實例,然而
// 其實在接下來還會繼續被包裝成SuspendLambda類對象
, (Function2)((Function2)new Function2<CoroutineScope, Continuation<? super Unit>, Object>(null){
// 狀態標志位
int label;
@Nullable
// 我們寫的lambda表達式被放在了invokeSuspend方法里執行
public final Object invokeSuspend(@NotNull Object object) {
IntrinsicsKt.getCOROUTINE_SUSPENDED();
switch (this.label) {
case 0: {
ResultKt.throwOnFailure((Object)object);
// 我們在協程里就做了一件事執行println
System.out.println((Object)"launch test");
return Unit.INSTANCE;
}
}
throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
}
// 注意這個函數,就是上面createCoroutineUnintercepted函數里調用到的地方
@NotNull
public final Continuation<Unit> create(@Nullable Object value, @NotNull Continuation<?> $completion) {
return (Continuation)new /* invalid duplicate definition of identical inner class */;
}
@Nullable
public final Object invoke(@NotNull CoroutineScope p1, @Nullable Continuation<? super Unit> p2) {
return (this.create((Object)p1, p2)).invokeSuspend((Object)Unit.INSTANCE);
}
}), (int)2, null);
繼續跟蹤SuspendLambda類及其父類鏈:
// 構造函數的入參completion,也是個續體,注意后面的分析,這個續體是誰
internal abstract class SuspendLambda(
public override val arity: Int,
completion: Continuation<Any?>?
) : ContinuationImpl(completion), FunctionBase<Any?>, SuspendFunction {
constructor(arity: Int) : this(arity, null)
public override fun toString(): String =
if (completion == null)
Reflection.renderLambdaToString(this) // this is lambda
else
super.toString() // this is continuation
}
// ContinuationImpl抽象類
internal abstract class ContinuationImpl(
completion: Continuation<Any?>?,
private val _context: CoroutineContext?
) : BaseContinuationImpl(completion) {
// _context協程上下文用的入參completion的上下文
constructor(completion: Continuation<Any?>?) : this(completion, completion?.context)
public override val context: CoroutineContext
get() = _context!!
@Transient
private var intercepted: Continuation<Any?>? = null
// 重點關注這個方法,這個方法先取出了completion上下文中的續體攔截器,然后調用了interceptContinuation方法
public fun intercepted(): Continuation<Any?> =
intercepted
?: (context[ContinuationInterceptor]?.interceptContinuation(this) ?: this)
.also { intercepted = it }
protected override fun releaseIntercepted() {
val intercepted = intercepted
if (intercepted != null && intercepted !== this) {
context[ContinuationInterceptor]!!.releaseInterceptedContinuation(intercepted)
}
this.intercepted = CompletedContinuation // just in case
}
}
// BaseContinuationImpl抽象類,注意看實現了Continuation接口,說明我們寫的協程方法體也是一個續體Continuation
internal abstract class BaseContinuationImpl(
// This is `public val` so that it is private on JVM and cannot be modified by untrusted code, yet
// it has a public getter (since even untrusted code is allowed to inspect its call stack).
public val completion: Continuation<Any?>?
) : Continuation<Any?>, CoroutineStackFrame, Serializable {
// This implementation is final. This fact is used to unroll resumeWith recursion.
// Continuation的核心方法
public final override fun resumeWith(result: Result<Any?>) {
// This loop unrolls recursion in current.resumeWith(param) to make saner and shorter stack traces on resume
var current = this
var param = result
while (true) {
// Invoke "resume" debug probe on every resumed continuation, so that a debugging library infrastructure
// can precisely track what part of suspended callstack was already resumed
probeCoroutineResumed(current)
with(current) {
val completion = completion!! // fail fast when trying to resume continuation without completion
val outcome: Result<Any?> =
try {
// 核心代碼,調用了invokeSuspend函數,前面分析過這個函數里放的就是我們寫的協程方法體block!!!
val outcome = invokeSuspend(param)
if (outcome === COROUTINE_SUSPENDED) return
Result.success(outcome)
} catch (exception: Throwable) {
Result.failure(exception)
}
releaseIntercepted() // this state machine instance is terminating
if (completion is BaseContinuationImpl) {
// unrolling recursion via loop
current = completion
param = outcome
} else {
// top-level completion reached -- invoke and return
completion.resumeWith(outcome)
return
}
}
}
}
// 在上面的反編譯的java有對該方法的實現
protected abstract fun invokeSuspend(result: Result<Any?>): Any?
protected open fun releaseIntercepted() {
// does nothing here, overridden in ContinuationImpl
}
public open fun create(completion: Continuation<*>): Continuation<Unit> {
throw UnsupportedOperationException("create(Continuation) has not been overridden")
}
// 在上面的反編譯的java有對該方法的實現
public open fun create(value: Any?, completion: Continuation<*>): Continuation<Unit> {
throw UnsupportedOperationException("create(Any?;Continuation) has not been overridden")
}
public override fun toString(): String =
"Continuation at ${getStackTraceElement() ?: this::class.java.name}"
// --- CoroutineStackFrame implementation
public override val callerFrame: CoroutineStackFrame?
get() = completion as? CoroutineStackFrame
public override fun getStackTraceElement(): StackTraceElement? =
getStackTraceElementImpl()
}
做個小總結:協程啟動函數里的協程體lambda代碼塊實際編譯的時候會被封裝成SuspendLambda對象,而SuspendLambda的繼承鏈路是這樣的:
SuspendLambda->ContinuationImpl->BaseContinuationImpl->Continuation
說明協程體lambda代碼塊也是個續體Continuation,并且在協程啟動方法里會調用自身實現的create(value: Any?, completion: Continuation<>)方法創建實例
繼續回到協程啟動流程:
public fun <T> (suspend () -> T).startCoroutineCancellable(completion: Continuation<T>): Unit = runSafely(completion) {
// 不難發現,協程的啟動就是由續體串起來的,記住每個調用的時候是哪個續體實例,就能記住協程的啟動流程
createCoroutineUnintercepted(completion) // 第一步,將block轉為SuspendLambda實例,此實例持有了completion,也就持有了創建協程時傳入的上下文,包括攔截器
.intercepted() // 第二步,調用了intercepted()方法,這個方法是個Continuation擴展函數
.resumeCancellableWith(Result.success(Unit)) // 第三步,這個函數依然是Continuation擴展函數
}
// 第二步的擴展函數intercepted()
public actual fun <T> Continuation<T>.intercepted(): Continuation<T> =
// 調用它的對象正是SuspendLambda,它就是ContinuationImpl的子類,而從上面的ContinuationImpl源碼我們可以
//看到,ContinuationImpl的intercepted()方法實際是調用了其持有的completion上下文中的續體攔截器,然后調用了interceptContinuation方法。
// 再往上面看這個completion正是協程啟動launch方法里創建的StandaloneCoroutine,它里面的上下文屬于攔截器性質的就是我們之前研究的Dispatchers.DEFAULT。
// 由此深挖到最終執行了CoroutineDispatcher類里的interceptContinuation(continuation: Continuation<T>): Continuation<T>方法,它返回了一個
// 經過包裝后的DispatchedContinuation實例,它也是個續體Continuation,后面會繼續分析
(this as? ContinuationImpl)?.intercepted() ?: this
// 第三步的擴展函數resumeCancellableWith()
public fun <T> Continuation<T>.resumeCancellableWith(
result: Result<T>,
onCancellation: ((cause: Throwable) -> Unit)? = null
): Unit = when (this) {
// 經過第二步分析我們知道最終我們得到的就是一個DispatchedContinuation對象,所以執行了resumeCancellableWith(result, onCancellation)方法
is DispatchedContinuation -> resumeCancellableWith(result, onCancellation)
else -> resumeWith(result)
}
接下來看看DispatchedContinuation類,重點看resumeCancellableWith方法:
// 注意看它的構造方法參數,一個CoroutineDispatcher和Continuation,從CoroutineDispatcher類里interceptContinuation(continuation: Continuation<T>)方法
// 可以看到,構建DispatchedContinuation傳入的dispatcher就是自己(this),而傳入的continuation就是由我們寫的協程lambda表達式封裝成的SuspendLambda。
internal class DispatchedContinuation<in T>(
@JvmField val dispatcher: CoroutineDispatcher,
@JvmField val continuation: Continuation<T>
) : DispatchedTask<T>(MODE_UNINITIALIZED), CoroutineStackFrame, Continuation<T> by continuation {
// delegate是自己,其實就是代理的continuation在干活
override val delegate: Continuation<T>
get() = this
inline fun resumeCancellableWith(
result: Result<T>,
noinline onCancellation: ((cause: Throwable) -> Unit)?
) {
val state = result.toState(onCancellation)
if (dispatcher.isDispatchNeeded(context)) {
_state = state
resumeMode = MODE_CANCELLABLE
// 關鍵步驟,在這里執行了調度方法dispatch,前面我們分析過dispatcher的dispatch方法就是往線程池里調度了一個runnable,真正
// 執行的地方是run方法,而這個runnable傳的是this,說明DispatchedContinuation是實現了Runnable接口的,而實現的地方其實是在其父類DispatchedTask
dispatcher.dispatch(context, this)
} else {
executeUnconfined(state, MODE_CANCELLABLE) {
if (!resumeCancelled(state)) {
resumeUndispatchedWith(result)
}
}
}
}
...略
}
// 父類DispatchedTask,父類SchedulerTask繼續往上找繼承關系就能找到Runnable接口,這里不深究線程池,會另外開篇講
internal abstract class DispatchedTask<in T>(
@JvmField public var resumeMode: Int
) : SchedulerTask() {
internal abstract val delegate: Continuation<T>
// 關鍵方法run
public final override fun run() {
assert { resumeMode != MODE_UNINITIALIZED } // should have been set before dispatching
val taskContext = this.taskContext
var fatalException: Throwable? = null
try {
val delegate = delegate as DispatchedContinuation<T>
// 結合前面的代碼,這個continuation是SuspendLambda對象
val continuation = delegate.continuation
withContinuationContext(continuation, delegate.countOrElement) {
val context = continuation.context
val state = takeState() // NOTE: Must take state in any case, even if cancelled
val exception = getExceptionalResult(state)
/*
* Check whether continuation was originally resumed with an exception.
* If so, it dominates cancellation, otherwise the original exception
* will be silently lost.
*/
val job = if (exception == null && resumeMode.isCancellableMode) context[Job] else null
if (job != null && !job.isActive) {
// 異常處理
val cause = job.getCancellationException()
cancelCompletedResult(state, cause)
continuation.resumeWithStackTrace(cause)
} else {
if (exception != null) {
continuation.resumeWithException(exception)
} else {
// 正常流程走到這里,調用resume方法,resume也是個擴展函數,真正執行的是continuation的resumeWith方法
// 前面分析過SuspendLambda的resumeWith方法是其父類BaseContinuationImpl實現的,里面執行了invokeSuspend()方法,
// 至此,我們定義的協程體方法終于得到了執行。
continuation.resume(getSuccessfulResult(state))
}
}
}
} catch (e: Throwable) {
// This instead of runCatching to have nicer stacktrace and debug experience
fatalException = e
} finally {
val result = runCatching { taskContext.afterTask() }
handleFatalException(fatalException, result.exceptionOrNull())
}
}
...略
}
// resume也是個擴展函數
public inline fun <T> Continuation<T>.resume(value: T): Unit =
resumeWith(Result.success(value))
至此,在各個續體饒了一大圈終于梳理完了協程是怎么工作的,此篇文章主要是理清楚協程的一些基本概念和運轉流程,還沒涉及到掛起和恢復和協程線程池的管理,下篇將會對協程的掛起和恢復進行剖析。
附:流程圖