1. Kotlin協程作用
Kotlin協程是一套基於Java Thread的線程框架,最大的特點就是可以1,用同步的方式寫出異步代碼,並且2,不阻塞當前線程。
2. cps轉換
2.1 cps轉換示例
//編譯前
private suspend fun testCPS(): String {
withContext(Dispatchers.IO) {
return "testCPS"
}
}
//編譯後
private final Object testCPS(Continuation $completion) {
return "testCPS";
}
Kotlin 的編譯器檢測到 suspend 關鍵字修飾的函數後,會進行cps轉換,轉換點:
1,函數中增加了一個Continuation類型的參數;
2,函數返回值變爲Object(本例String->Object)
注意:這裏suspend函數即使沒有使用withContext開啓一個協程,也會進行cps轉換。說明只要suspend函數,不管開不開啓協程,編譯器都會對其進行cps轉換。
2.2 參數Continuation
public interface Continuation<in T> {
public val context: CoroutineContext
// 相當於 onSuccess 結果
// ↓ ↓
public fun resumeWith(result: Result<T>)
}
interface CallBack {
void onSuccess(String response);
}
1,Continuation :續體,可以理解爲剩餘要執行的代碼。協程體中的異步操作被狀態機分割成不同的片段,分片段執行,執行完一部分,剩下的部分叫做續體。
2,Continuation 是一個接口,和一般回調接口定義類似,可以判斷,協程的思想其實就是回調。
Continuation 定義看一個協程上下文屬性context,一個方法聲明resumeWith(),用於協程1,啓動(DispatchedContinuation),2,掛起時恢復(BaseContinuationImpl),或者3,協程運行完成時的回調(AbstractCoroutine);
2.2 返回值 Object
爲什麼返回值從String->Object?
public suspend fun <T> withContext(
context: CoroutineContext,
block: suspend CoroutineScope.() -> T
): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return suspendCoroutineUninterceptedOrReturn sc@ { uCont ->
...
val coroutine = DispatchedCoroutine(newContext, uCont)
coroutine.initParentJob()
block.startCoroutineCancellable(coroutine, coroutine)
coroutine.getResult()
}
}
fun getResult(): Any? {
if (trySuspend()) return COROUTINE_SUSPENDED
// otherwise, onCompletionInternal was already invoked & invoked tryResume, and the result is in the state
val state = this.state.unboxState()
if (state is CompletedExceptionally) throw state.cause
@Suppress("UNCHECKED_CAST")
return state as T
}
可以看出withContext的返回值有兩種:
1,如果當前withContext中的異步操作沒有完成,返回COROUTINE_SUSPENDED,協程框架根據這個字段掛起協程(其實就是直接return);
2,如果當前withContext中的異步操作已經完成,返回對應操作執行後的返回值(對應String)
3. 協程狀態機
3.1 協程狀態機
//編譯前
class TestCoroutine {
private fun startCoroutine() {
// funTest協程體
val funTest: suspend CoroutineScope.() -> Unit = {
println("funTest")
suspendFun1()
suspendFun2()
}
GlobalScope.launch(Dispatchers.Default, block = funTest)
}
// 掛起函數
suspend fun suspendFun1() {
println("suspendFun1")
}
// 掛起函數
suspend fun suspendFun2() {
println("suspendFun2")
}
}
//編譯後
public final class TestCoroutine {
private final void startCoroutine() {
Function2 funTest = (Function2)(new Function2((Continuation)null) {
int label;
@Nullable
public final Object invokeSuspend(@NotNull Object $result) {
Object var4 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
TestCoroutine var10000;
switch(this.label) {
case 0:
ResultKt.throwOnFailure($result);
String var2 = "funTest";
boolean var3 = false;
System.out.println(var2);
var10000 = TestCoroutine.this;
this.label = 1;
if (var10000.suspendFun1(this) == var4) {
return var4;
}
break;
case 1:
ResultKt.throwOnFailure($result);
break;
case 2:
ResultKt.throwOnFailure($result);
return Unit.INSTANCE;
default:
throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
}
var10000 = TestCoroutine.this;
this.label = 2;
if (var10000.suspendFun2(this) == var4) {
return var4;
} else {
return Unit.INSTANCE;
}
}
@NotNull
public final Continuation create(@Nullable Object value, @NotNull Continuation completion) {
Intrinsics.checkNotNullParameter(completion, "completion");
Function2 var3 = new <anonymous constructor>(completion);
return var3;
}
public final Object invoke(Object var1, Object var2) {
return ((<undefinedtype>)this.create(var1, (Continuation)var2)).invokeSuspend(Unit.INSTANCE);
}
});
BuildersKt.launch$default((CoroutineScope)GlobalScope.INSTANCE, (CoroutineContext)Dispatchers.getDefault(), (CoroutineStart)null, funTest, 2, (Object)null);
}
@Nullable
public final Object suspendFun1(@NotNull Continuation $completion) {
System.out.println(""suspendFun1"");
return Unit.INSTANCE;
}
@Nullable
public final Object suspendFun2(@NotNull Continuation $completion) {
System.out.println("suspendFun2");
return Unit.INSTANCE;
}
}
(其他文章描述)在反編譯的代碼中,協程體funTest被編譯成一個繼承SuspendLambda的類,在類中實現create(),invokeSuspend()兩個方法:
create()創建了一個協程體funTest類的實例;
invokeSuspend()方法執行具體的協程操作。
(我反編譯的代碼)沒搞懂的地方:
1,只創建了一個Function2的對象;
2,create方法創建的是匿名對象(anonymous constructor);
3,invoke方法是什麼作用
基本原理:
協程體會被編譯成一個SuspendLambda的子類,在這個類的invokeSuspend方法中,協程體中的suspend方法會被分割到switch不同的分支中,每個suspend方法會被cps機制轉換成帶有一個Continuation參數的方法。
通過一個label標籤控制分支代碼執行,label爲0,首先會進入第一個分支,首先將label設置爲下一個分支的數值,然後執行第一個suspend方法並傳遞當前Continuation,得到返回值,如果是COROUTINE_SUSPENDED,協程框架就直接return,協程掛起,當第一個suspend方法執行完成,會回調Continuation的invokeSuspend方法,進入第二個分支執行,以此類推執行完所有suspend方法。
如果suspend方法直接返回執行結果,那invokeSuspend後面的代碼怎麼執行?
3.2 SuspendLambda類圖
4 協程相關概念
4.1 CoroutineContext
launch函數是CoroutineScope的一個擴展函數,CoroutineScope只是一個接口,但是可以通過CoroutineScope的擴展方法進行協程的創建,除了launch函數還有async函數。
CoroutineScope除了通過擴展函數創建協程還有其它兩個作用,launch函數返回一個Job對象,可以通過這個Job管理協程,另外CoroutineScope爲協程提供一個上下文CoroutineContext。
CoroutineContext協程的上下文,這是一個數據集合接口聲明,協程中Job、Dispatcher調度器都可以是它的元素,CoroutineContext有一個非常好的作用就是我們可以通過它拿到Job、Dispatcher調度器等數據。
CombinedContext是CoroutineContext接口的具體實現類,存在兩個屬性,其中element是一個Element,代表集合的元素,left是一個CoroutineContext,代表鏈表的下一個節點。
通過CoroutineContext#plus可以看出,CoroutineContext的數據存儲方式是一個左向鏈表,鏈表的每一個節點是CombinedContext,並且存在攔截器的情況下,攔截器永遠是鏈表尾部的元素,這樣設計目的是因爲攔截器的使用頻率很高,爲了更快的讀取攔截器;
沒看懂這個左向鏈表實現,現在只要知道這是個集合,類似list,但是它有一個left元素始終在表尾,存儲攔截器,
4.2 CoroutineStart 啓動模式
CoroutineStart 是協程的啓動模式,存在以下4種模式:
DEFAULT 立即調度,可以在執行前被取消
LAZY 需要時才啓動,需要start、join等函數觸發纔可進行調度
ATOMIC 立即調度,協程肯定會執行,執行前不可以被取消
UNDISPATCHED 立即在當前線程執行,直到遇到第一個掛起點(可能切線程)
5. 協程啓動
// launch是CoroutineScope的一個擴展函數
public fun CoroutineScope.launch(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.()
): Job {
// CoroutineContext創建一個新的Context
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函數存在3個參數:
CoroutineContext 協程的上下文
CoroutineStart 協程的啓動模式
suspend CoroutineScope.() -> Unit 協程體
newCoroutineContext()是CoroutineScope的一個擴展方法,它的作用就是將傳參context與CoroutineScope中的CoroutineContext集合合併,並返回一個新的CoroutineContext,如果傳入Dispatchers.Default,就是將Dispatchers.Default與CoroutineScope中的CoroutineContext合併。
根據啓動模式,構建一個AbstractCoroutine的子類(協程對象都繼承AbstractCoroutine),如果是默認模式,則創建StandaloneCoroutine,並調用它的start方法。並將StandaloneCoroutine又作爲job返回。
5.1 AbstractCoroutine
AbstractCoroutine繼承或者實現了JobSupport、Job、Continuation、CoroutineScope。
JobSupport是Job的具體實現,AbstractCoroutine可以作爲一個Job控制協程的生命週期,同時實現Continuation接口,也可以作爲一個Continuation,重寫的resmueWith()方法的一個重要作用是恢復協程
AbstractCoroutine#resmueWith
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)
// JobSupport#afterCompletion
protected open fun afterCompletion(state: Any?) {}
在AbstractCoroutine#resmueWith中首先根據JobSupport#makeCompletingOnce返回狀態判斷,協程是否處於等待子協程完成的狀態:
state == COMPLETING_WAITING_CHILDREN 等待子協程完成,自身才可完成。子協程完成後,觸發afterCompletion()
state != COMPLETING_WAITING_CHILDREN 沒有子協程或者所有子協程已經完成,自身可以完成,直接觸發afterCompletion()
協程對象可以通過重寫afterCompletion()處理協程完成之後的操作,下文中的協程恢復章節中,withContext()中DispatchedCoroutine協程對象,通過afterCompletion()恢復了外層的協程的運行。
AbstractCoroutine#start()
public fun <R> start(start: CoroutineStart, receiver: R, block: suspend R.() -> T) {
...
//block :協程體 //receiver:協程對象 //this:AbstractCoroutine(也是協程對象)
start(block, receiver, this)
}
CoroutineStart#invoke
public operator fun <R, T> invoke(block: suspend R.() -> T, receiver: R, completion: Continuation<T>): Unit =
when (this) {
//completion:start傳過來的AbstractCoroutine
DEFAULT -> block.startCoroutineCancellable(receiver, completion)
ATOMIC -> block.startCoroutine(receiver, completion)
UNDISPATCHED -> block.startCoroutineUndispatched(receiver, completion)
LAZY -> Unit // will start lazily
}
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)
}
AbstractCoroutine#start()調用了CoroutineStart的invoke()方法,然後根據啓動模式調用block對應的協程啓動方法,block.startCoroutineCancellable(receiver, completion) 中是一個鏈式調用流程。
createCoroutineUnintercepted()
public actual fun <T> (suspend () -> T).createCoroutineUnintercepted(
completion: Continuation<T>
): Continuation<Unit> {
// probeCompletion :AbstractCoroutine
val probeCompletion = probeCoroutineCreated(completion)
return if (this is BaseContinuationImpl)
create(probeCompletion)
else
createCoroutineFromSuspendFunction(probeCompletion) {
(this as Function1<Continuation<T>, Any?>).invoke(it)
}
}
// continuation:AbstractCoroutine
public final Continuation<Unit> create(Object obj, Continuation<?> continuation) {
...
MainActivity$startCoroutine$funTest$1 mainActivity$startCoroutine$funTest$1 = new MainActivity$startCoroutine$funTest$1(this.this$0, continuation);
return mainActivity$startCoroutine$funTest$1;
}
createCoroutineUnintercepted中通過調用create(probeCompletion)創建了一個協程體類的對象。
createCoroutineUnintercepted()是一個擴展函數,通過協程體block調用,所以源碼中this is BaseContinuationImpl的判斷中this指協程體類,編譯章節中協程體被編譯成SuspendLambda的子類;這裏的create函數就是SuspendLambda的子類中的create函數。
這裏的block對象和協程體類對象是什麼關係?有什麼區別?
注意看下構造函數的參數continuation,這裏continuation就是AbstractCoroutine,在協程體類的繼承鏈中,這個continuation一直傳遞到了BaseContinuationImpl父類中,用於後續協程的恢復。
注意:這裏將AbstractCoroutine對象傳遞給了協程類對象,進行了第一層代理。
繼續分析intercepted()
public actual fun <T> Continuation<T>.intercepted(): Continuation<T> =
(this as? ContinuationImpl)?.intercepted() ?: this
ContinuationImpl#intercepted
public fun intercepted(): Continuation<Any?> =
intercepted
?: (context[ContinuationInterceptor]?.interceptContinuation(this) ?: this) .also { intercepted = it }
CoroutineDispatcher#interceptContinuation
public final override fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T> =
//this是Dispatcher,continuation是協程體類對象
DispatchedContinuation(this, continuation)
intercepted()協程體類對象轉換成ContinuationImpl,然後調用了ContinuationImpl的intercepted方法,intercepted方法中調用context[ContinuationInterceptor] 從協程類對象的CoroutineContext集合中取到調度器CoroutineDispatcher(這個CoroutineContext是launch是構建的,並傳遞到StandaloneCoroutine對象中),並調用調度器CoroutineDispatcher的interceptContinuation(),interceptContinuation()的作用是將協程體Continuation對象包裝成一個DispatchedContinuation。
注意:這裏將協程類對象傳遞給了DispatchedContinuationd對象,進行了第二層代理
5.2 CoroutineDispatcher
CoroutineDispatcher
public abstract class CoroutineDispatcher :
AbstractCoroutineContextElement(ContinuationInterceptor), ContinuationInterceptor {
...
// 是否需要線程調度
public open fun isDispatchNeeded(context: CoroutineContext): Boolean = true
// 線程調度,讓一個runable對象在指定線程運行
public abstract fun dispatch(context: CoroutineContext, block: Runnable)
// 將協程體對象continuation封裝爲一個DispatchedContinuation對象
public final override fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T> =
DispatchedContinuation(this, continuation)
...
}
CoroutineDispatcher的作用是進行任務的線程切換。
CoroutineDispatcher實現了ContinuationInterceptor,代表是一個攔截器;
實現CoroutineContext接口,存儲在CoroutineContext的left節點。
CoroutineContext[ContinuationInterceptor]就可以在CoroutineContext集合中獲取到攔截器。
爲啥要通過攔截器去代理Continuation,直接使用DispatchedContinuation包裝不就行了嗎??
5.3 DispatchedContinuation
接着分析resumeCancellableWith(Result.success(Unit), onCancellation)
DispatchedContinuation
internal class DispatchedContinuation<in T>(
// 調度器
@JvmField val dispatcher: CoroutineDispatcher,
// 協程體Continuation對象
@JvmField val continuation: Continuation<T>
) : DispatchedTask<T>(MODE_ATOMIC_DEFAULT), CoroutineStackFrame, Continuation<T> by continuation {
// 使用delegate存儲當前對象
override val delegate: Continuation<T>
get() = this
// ATOMIC啓動模式
override fun resumeWith(result: Result<T>) {
val context = continuation.context
val state = result.toState()
// 是否需要線程調度
if (dispatcher.isDispatchNeeded(context)) {
_state = state
resumeMode = MODE_ATOMIC_DEFAULT
// dispatch 調度線程,第二個參數是一個Runnable類型,這裏傳參this也就是DispatchedContinuation自身
// DispatchedContinuation實際上也是一個Runnable對象,調用調度器的dispatch方法之後就可以使這個runnable在指定的線程運行了
dispatcher.dispatch(context, this)
} else {
executeUnconfined(state, MODE_ATOMIC_DEFAULT) {
withCoroutineContext(this.context, countOrElement) {
// 不需要調度,執行協程體的resumeWith
continuation.resumeWith(result)
}
}
}
}
// 默認啓動模式
inline fun resumeCancellableWith(result: Result<T>) {
val state = result.toState()
if (dispatcher.isDispatchNeeded(context)) {
_state = state
resumeMode = MODE_CANCELLABLE
dispatcher.dispatch(context, this)
} else {
executeUnconfined(state, MODE_CANCELLABLE) {
if (!resumeCancelled()) {
resumeUndispatchedWith(result)
}
}
}
}
}
DispatchedContinuation 代理協程體類對象(SuspendLambda)並持有線程調度器(CoroutineDispatcher),它的作用就是使用線程調度器將協程體調度到指定的線程執行。
DispatchedContinuation也實現了Continuation接口,並重寫resumeWith(),首先
根據dispatcher.isDispatchNeeded(context)判斷需要線程切換:
1.如果需要線程調度,則調用dispatcher#dispatch進行調度,而dispatch()的第二個參數是一個runnable對象(這裏傳參爲this,即DispatchedContinuation對象本身,DispatchedContinuation同時還實現了Runnable接口),這個runnable就會運行在調度的線程上;
2.不需要調度則直接調用協程體類continuation對象的resumeWith(),前面的章節中提到,協程體的運行就是協程體類Continuation對象的resumeWith()被觸發,所以這裏就會讓協程體在當前線程運行;
另外還有一個方法resumeCancellableWith(),它和resumeWith()的實現很類似,在不同的啓動模式下調度線程的方法調用不同。比如默認的啓動模式調用resumeCancellableWith(),ATOMIC啓動模式則調用resumeWith()。
public actual object Dispatchers {
@JvmStatic
public actual val Default: CoroutineDispatcher = createDefaultDispatcher()
@JvmStatic
public actual val Main: MainCoroutineDispatcher get() = MainDispatcherLoader.dispatcher
@JvmStatic
public actual val Unconfined: CoroutineDispatcher = kotlinx.coroutines.Unconfined
@JvmStatic
public val IO: CoroutineDispatcher = DefaultScheduler.IO
}
internal actual fun createDefaultDispatcher(): CoroutineDispatcher =
if (useCoroutinesScheduler) DefaultScheduler else CommonPool
可以看到Default和IO 底層還是使用了線程池進行調度;Main使用了handler進行調度。
5.4 DispatchedTask
internal abstract class DispatchedTask<in T>(
@JvmField public var resumeMode: Int
) : SchedulerTask() {
’
// 在DispatchedContinuation中重寫了該屬性,delegate實際是指DispatchedContinuation對象
internal abstract val delegate: Continuation<T>
public final override fun run() {
...
val delegate = delegate as DispatchedContinuation<T>
// 通過delegate拿到原始協程體Continuation對象
val continuation = delegate.continuation
...
// 調用協程體類對象的resume
continuation.resume(getSuccessfulResult(state))
...
}
}
// Continuation的擴展方法,觸發Continuation內的方法resumeWith
public inline fun <T> Continuation<T>.resume(value: T): Unit =
resumeWith(Result.success(value))
DispatchedTask的run方法中調用了協程體類對象的resume方法,間接調用了BaseContinuationImpl的resumeWith
注意:這裏是協程體類的resumeWith被執行了,不是DispatchedContinuation
5.5 BaseContinuationImpl
internal abstract class BaseContinuationImpl(
// completion:實參是一個AbstractCoroutine
public val completion: Continuation<Any?>?
) : Continuation<Any?>, CoroutineStackFrame, Serializable {
public final override fun resumeWith(result: Result<Any?>) {
var current = this
var param = result
while (true) {
probeCoroutineResumed(current)
with(current) {
val completion = completion!!
val outcome: Result<Any?> =
try {
// 調用invokeSuspend方法,協程體真正開始執行
val outcome = invokeSuspend(param)
// invokeSuspend方法返回值爲COROUTINE_SUSPENDED,resumeWith方法被return,結束執行,說明執行了掛起操作
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) {
current = completion
param = outcome
} else {
// 在示例代碼中,completion是一個AbstractCoroutine,是指launch函數創建的StandaloneCoroutine
completion.resumeWith(outcome)
return
}
}
}
}
protected abstract fun invokeSuspend(result: Result<Any?>): Any?
}
BaseContinuationImpl定義了一個抽象方法invokeSuspend(),並重寫了Continuation的resumeWith(),並在其中調用invokeSuspend(),具體實現就是SuspendLambda的invokeSuspend(),invokeSuspend()方法中便是具體協程任務。
invokeSuspend()和invoke啥關係??
5.6 協程啓動流程總結:
以調度器爲Dispatchers.Default,啓動模式爲CoroutineStart.DEFAULT爲例:
CoroutineScope#launch()創建一個協程,在其內部實現中根據啓動模式爲CoroutineStart.DEFAULT,創建一個StandaloneCoroutine協程對象,並觸發StandaloneCoroutine#start(start, coroutine, block);
StandaloneCoroutine的父類是AbstractCoroutine,StandaloneCoroutine#start()的實現在其父類中,即AbstractCoroutine#start();
在AbstractCoroutine#start()中,觸發CoroutineStart#invoke();
CoroutineStart#invoke()的處理邏輯中,根據調度器爲Dispatchers.Default,調用協程體的startCoroutineCancellable()方法;
startCoroutineCancellable()的內部處理是一個鏈式調用:
createCoroutineUnintercepted(..).intercepted().resumeCancellableWith(Result.success(Unit))
createCoroutineUnintercepted()創建一個協程體類對象;
intercepted()使用攔截器(調度器)將協程體類對象包裝成DispatchedContinuation(DispatchedContinuation代理了協程體類Continuation對象,並持有調度器);
調用DispatchedContinuation#resumeCancellableWith()。
在DispatchedContinuation#resumeCancellableWith()中,使用線程調度器觸發dispatcher#dispatch(context, this)進行調度,該調度器爲Dispatchers.Default;
Dispatchers.Default#dispatch()調度處理中,將DispatchedContinuation分發到CoroutineScheduler線程池中,由CoroutineScheduler分配一個線程Worker,最終在Woreder的run()方法中觸發了DispatchedContinuation的run(),其內部實現是使協程體Continuation對象的resumeWithI()得以執行,前文中分析到協程體的執行其實就是resumeWith()方法被調用,這樣協程體就可以在執行的線程中執行了。
協程啓動流程圖:
5.7 三個協程對象總結:
1,第一層協程對象AbstractCoroutine,主要處理協程狀態和恢復掛起協程。
2,第二層對象BaseContinuationImpl,由編譯器將我們的代碼轉換而成,利用狀態機實現代碼分段執行和協程掛起,並代理第一層對象。在其resumeWith方法中通過調用invokeSuspend()執行我們的任務代碼。
注意:resumeWith方法中調用completion.resumeWith(outcome)是恢復協程,這個completion是AbstractCoroutine(launch對應StandaloneCoroutine或者witchContext對應DispatchedCoroutine),不是BaseContinuationImpl的子類。
3,第三層對象DispatchedContinuation,對第二層協程對象進行代理,負責使用dispatcher進行調度任務。
注意:協程體類是持有了一個AbstractCoroutine,不是繼承
6.協程掛起
withContext()
public suspend fun <T> withContext(
context: CoroutineContext,
block: suspend CoroutineScope.() -> T
): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
// 返回啓動withContext的協程體
return suspendCoroutineUninterceptedOrReturn sc@ { uCont ->
// 構建一個新的newContext,合併當前協程體以及withContext協程體的CoroutineContext
val oldContext = uCont.context
val newContext = oldContext + context
// 檢查協程是否活躍,如果線程處於非活躍的狀態,拋出cancle異常
newContext.checkCompletion()
...
// DispatchedCoroutine也是一個AbstractCoroutine對象,負責協程完成的回調,
// 注意這裏的Continuation的傳參爲uCont,及發起withContext的協程對象
val coroutine = DispatchedCoroutine(newContext, uCont)
coroutine.initParentJob()
// 和協程啓動的流程一樣,啓動withContext的協程
// 注意這裏的傳參coroutine爲DispatchedCoroutine,它持有需要恢復的協程
block.startCoroutineCancellable(coroutine, coroutine)
// 返回結果爲掛起還是完成
coroutine.getResult()
}
}
在withContext()的源碼可以看到,withContext()的協程體的啓動和原有協程的啓動流程是一樣的,DispatchedCoroutin是AbstractCoroutine的一個子類,並且在創建DispatchedCoroutin時的傳參是外層協程體對象,這是因爲當withContext()的協程體完成的時候需要通過外層協程體對象恢復當前協程的運行。
先看下協程的掛起coroutine.getResult()的實現。
// DispatchedCoroutine#getResult
fun getResult(): Any? {
// 返回COROUTINE_SUSPENDED,掛起
if (trySuspend()) return COROUTINE_SUSPENDED
val state = this.state.unboxState()
// 出現異常
if (state is CompletedExceptionally) throw state.cause
@Suppress("UNCHECKED_CAST")
// 未出現異常結果返回
return state as T
}
// DispatchedCoroutine#trySuspend
private val _decision = atomic(UNDECIDED)
private fun trySuspend(): Boolean {
_decision.loop { decision ->
when (decision) {
// compareAndSet原子操作,當前值與預期值一致時返回true,以原子方式更新自身的值
UNDECIDED -> if (this._decision.compareAndSet(UNDECIDED, SUSPENDED)) return true
RESUMED -> return false
else -> error("Already suspended")
}
}
}
是否掛起,結束協程運行,關鍵在是否返回COROUTINE_SUSPENDED標誌,在getResult()方法中的處理邏輯,就是看trySuspend()是否返回true。
trySuspend()方法中,_decision默認爲UNDECIDED,預期的參數值傳參也爲UNDECIDED,所以,trySuspend返回true,最終getResult方法返回了COROUTINE_SUSPENDED,協程被掛起了。
7.協程恢復
withContext()啓動一個協程和launch類似,當執行到BaseContinuationImpl的resumeWith方法,調用invokeSuspend得到結果之後,會調用內部代理的completion.resumeWith(outcome)方法,這個completion是DispatchedCoroutine。
DispatchedCoroutine是AbstractCoroutine的子類,當協程完成時會調用它的內部方法resumeWith(),內部的處理邏輯最後會觸發JubSpuuort#afterCompletion(),而在DispatchedCoroutine中重寫了afterCompletion()。
private class DispatchedCoroutine<in T>(
context: CoroutineContext,
// 外部需要恢復的協程
uCont: Continuation<T>
) : ScopeCoroutine<T>(context, uCont) {
override fun afterCompletion(state: Any?) {
afterResume(state)
}
override fun afterResume(state: Any?) {
// 在getResult()之前,協程已運行結束,未發生掛起,不需要恢復外層協程
if (tryResume()) return
// 獲取外部協程的DispatchedContinuation,去恢復外層協程
uCont.intercepted().resumeCancellableWith(recoverResult(state, uCont))
}
}
uCont.intercepted()獲取到外層協程的DispatchedContinuation,然後調用resumeCancellableWith方法,使用外層協程的dispatcher將任務的執行切換到之前的線程中去執行。再次調用到外層協程的BaseContinuationImpl#resumeWith方法,再次調用到外層協程類的invokeSuspend方法中,去執行剩餘代碼。如果能直接結果就調用外層協程的completion.resumeWith(outcome)結束協程(completion是外層協類對象中代理的AbstractCoroutine)