Kotlin 協程源碼閱讀筆記 —— 協程工作原理

Kotlin 協程源碼閱讀筆記 —— 協程工作原理

Kotlin 協程在網上有很多的人把它吹得神乎其神,什么性能多么多么好,效率比線程高多少多少,balabala 一堆優點。首先在我看來協程和線程壓根兒就沒有可比性,就好像說姚明和劉翔誰更厲害一樣,線程是操作系統的調度的基本單位,線程也是 CPU 執行的一個基本任務;而協程只是在編程語言上定義的一種優化多線程通信、調度的一種編程方式(至少 Kotlin 中是這樣),而操作系統可不認識什么是協程,而協程中的任務最終也是在線程上執行。
Kotlin 協程上來說它的最大的優點只有一個它能夠以同步的方式來寫異步代碼,能夠干掉編程中的地獄回調(通過類似于 RxJava 流的編程方式也能夠干掉地獄回調,不過不是本篇文章中的討論內容),而它的其他優點也都是這一個優點的發散,不要小瞧這個優點,如果消除了各種異步 Callback,能夠在很大的程度上提高代碼的可閱讀性,減少 BUG 的產生,也更容易能夠定位到 BUG

簡單了協程的優點,后續就要看看它是怎么工作的了,那么準備就緒后就開啟今天的內容。

用 Callback 寫異步任務

假如我有以下的異步任務:

val delayExecutor: ScheduledThreadPoolExecutor by lazy {
    ScheduledThreadPoolExecutor(1)
}

fun delay(time: Long, callback: () -> Unit) {
    delayExecutor.schedule({ callback() }, time.coerceAtLeast(0), TimeUnit.MILLISECONDS)
}

我用 delay() 來實現一個異步耗時任務,實現就是通過 delayExecutor 添加一個定時任務,任務執行時就調用 callback

我有以下代碼要調用異步任務:

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        helloWorld {
            println(it)
        }
    }

    fun helloWorld(callback: (s: String) -> Unit) {
        hello { hello ->
            world { world ->
                callback("${hello}${world}")
            }
        }
    }

    fun hello(callback: (s: String) -> Unit) {
        delay(500) {
            callback("Hello, ")
        }
    }

    fun world(callback: (s: String) -> Unit) {
        delay(500) {
            callback("World!!")
        }
    }
}

我在 onCreate() 函數中調用了異步任務 helloWorld(),成功后會在 callback 中打印最后的結果。 helloWorld() 又由 hello()world() 兩個任務組成,hello() 任務成功后在調用 world() 任務,最后結合 hello()world() 兩個任務的結果的結果回調 helloWorld()callback

用協程寫異步任務

同樣是上面的異步任務,我用 Kotlin 協程改造一下:

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val coroutineScope = CoroutineScope(Dispatchers.Default)
        coroutineScope.launch {
            println(helloWorld())
        }
    }

    suspend fun helloWorld(): String {
        val hello = hello()
        val world = world()
        return hello + world
    }

    suspend fun hello(): String {
        return delaSuspend(500, "Hello, ")
    }

    suspend fun world(): String {
        return delaSuspend(500, "World!!")
    }

    suspend fun <T> delaSuspend(time: Long, data: T): T {
        return suspendCancellableCoroutine { cont ->
            delay(time) {
                cont.resumeWith(Result.success(data))
            }
        }
    }
}

首先我把上面的 delay() 耗時任務的回調改造成了協程 suspend 方法,具體的改造參考 delaSuspend() 方法的實現。然后 hello()world() 也都是 suspend() 方法,他們都是通過調用 delaSuspend() 來模擬異步任務,在 helloWrold() 中分別調用 hello()world() 方法,這里都是以同步的方式調用的哦,然后組合他們的結果然后返回。在 onCreate() 新建一個協程,然后也是直接以同步的方式調用 helloWorld()

和改造前的代碼有一個非常顯著的特點就是消滅的所有的 callback,所有的異步任務都是以同步的方式調用的,你可能也會吐槽也沒感覺比之前優化了多少,上面 demo 中的任務比較簡單,總共才 3 個 callback,而且也沒有處理異常的回調,callback 的層級最大也才 2。越復雜的任務協程的優勢就會越大。

Kotlin 協程工作原理

雖然在源碼中我們看到的協程代碼是同步的,其實虛擬機執行的時候它還是一個不折不扣的 callback,這主要歸功于 Kotlin 編譯器的處理,我們就以上面的 demo 來一步一步分析它的工作方式。

CoroutineScope

如果要開啟一個協程就需要先通過 CoroutineScope() 方法創建一個 CoroutineSope,在這個方法中可以指定我們的默認 CoroutineContext

public interface CoroutineScope {
    public val coroutineContext: CoroutineContext
}

CoroutineScope 的接口非常簡單,只需要實現一個 CoroutineContext

我們來看看 CoroutineScope() 方法的實現:

@Suppress("FunctionName")
public fun CoroutineScope(context: CoroutineContext): CoroutineScope =
    ContextScope(if (context[Job] != null) context else context + Job())

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)"
}

上面的代碼非常簡單就只是把我們添加的 CoroutineContext 設置到 CoroutineScope,這里注意添加了一個 JobCoroutineContext,每個協程啟動時都會創建一個 Job 對象,這些由 CoroutineScope 啟動的協程的 Job 都是 CoroutineScope 中的 Job 的子任務,而協程里面還可以再啟動子協程,這個子協程的 Job 的父 Job 就是啟動他的協程的 Job。所以通過 Job 就構成了一個任務的繼承鏈。當父 Job 取消后他的子 Job 也會被取消。所以如果是 CoroutineScope 中的頂級父 Job 取消了,那么用他啟動的所有的協程或者孫協程等等也都會被取消。CoroutineScope#cancel() 的方法實現就是通過調用 CoroutineContext 中的 Jobcancel() 方法實現的:

public fun CoroutineScope.cancel(cause: CancellationException? = null) {
    val job = coroutineContext[Job] ?: error("Scope cannot be cancelled because it does not have a job: $this")
    job.cancel(cause)
}

通常我們為了防止內存泄漏,在 Activity 或者 Fragment 或者一些什么別的組件退出后都會調用他們所對應的 CoroutineScope#cancel() 方法,避免內存泄漏。

啟動一個協程

我們啟動協程是通過 CoroutineScepe#launch() 擴展函數來完成的,其中的 Lambda 對象就是協程執行開始的第一個方法,我們來看看它的源碼實現:

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
}

這里會對傳入的 CoroutineContext 進行修改,處理的方法是 newCoroutineContext();然后根據啟動類型創建一個 coroutine 對象,默認的實現是 StandaloneCoroutine 類,然后調用 Coroutine#start() 方法,最后返回 Coroutine。這里要非常注意 StandaloneCoroutine 是一個 JobContinuation (很多人中文翻譯它為續體,后續會重點講),CoroutineScope(也就是他也能夠啟動協程,也就是當前協程的子協程)。后面我們會再看這些對象所處理的邏輯,現在有點懵也沒關系。

我們繼續看看 newCoroutineContext() 方法對傳入的 CoroutineContext 的處理:

@ExperimentalCoroutinesApi
public actual fun CoroutineScope.newCoroutineContext(context: CoroutineContext): CoroutineContext {
    val combined = foldCopies(coroutineContext, context, true)
    val debug = if (DEBUG) combined + CoroutineId(COROUTINE_ID.incrementAndGet()) else combined
    return if (combined !== Dispatchers.Default && combined[ContinuationInterceptor] == null)
        debug + Dispatchers.Default else debug
}

這里會添加一個 CoroutineIdCoroutineContext 用來記錄 Coroutine 的名字,其實就是修改協程運行時的線程的名字,添加上協程編號的信息;這里還會判斷是否有 CoroutineInterceptorCoroutineContext,如果沒有,使用 Dispatcher.Default 作為默認的 CoroutineInterceptor

然后簡單看看 StandaloneCoroutine 的實現:

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,它重寫了 handleJobException() 方法來處理協程的異常,處理調用的方法是 handleCoroutineException(),我們來看看它的實現:

@InternalCoroutinesApi
public fun handleCoroutineException(context: CoroutineContext, exception: Throwable) {
    // Invoke an exception handler from the context if present
    try {
        context[CoroutineExceptionHandler]?.let {
            it.handleException(context, exception)
            return
        }
    } catch (t: Throwable) {
        handleUncaughtCoroutineException(context, handlerException(exception, t))
        return
    }
    // If a handler is not present in the context or an exception was thrown, fallback to the global handler
    handleUncaughtCoroutineException(context, exception)
}

這里會查找 CoroutineContext 中是否有 CoroutineExceptionHandler,如果有異常就交給它來處理,如果沒有就由 GlobalHandler 來處理,默認就是崩潰啦。

然后就是調用 Coroutine#start() 方法來啟動一個協程了,看看它的代碼:

public fun <R> start(start: CoroutineStart, receiver: R, block: suspend R.() -> T) {
    start(block, receiver, this)
}

@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
    }

上面的 block 就是我們啟動協程時傳遞過來的 Lambda 對象,然后 receivercompletion 都是我們上面創建的 StandaloneCoroutine 對象。繼續看看 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)
    }

這里首先通過 createCoroutineUnintercepted() 方法將我們的 Lambda 對象構建成一個 Continuation 對象;然后調用 intercepted() 方法對原來的 Continuation 對象進行攔截器的處理;然后調用 resumeCancellableWith() 方法來觸發協程的開始。上面的三個方法都非常重要。

這里在開始之前非常有必要解釋一下 Continuation,很多人中文翻譯成續體,我就不用中文的名字了,還是繼續用英文來表示。前面我們說到協程的本質其實就是一個 Callback,而用來控制 Callback 回調成功/失敗就是 Continuation 很重要的一個職責,同時它還記錄了當前方法對應的執行的位置(像程序計數器一樣),上次執行后的中間結果等等。Continuation 還會涉及到兩個非常重要的概念那就是 suspendresume,中文翻譯成掛起和恢復,我還是使用英文名詞來表示他們。所謂的 suspend 其實就相當于我們調用了一個異步方法后等待 callback 時的協程狀態,這時由于 callback 還沒有回來當前的線程還可以做其他的任務;而 resume 就是 callback 回調成功需要喚醒原來的協程繼續執行。這也就是很多人說得很邪乎的掛起與恢復,說白了就是調用異步任務時就掛起,callback 成功或者失敗就是恢復,后面我們還會從源碼中看到他具體是怎么掛起和恢復的(其實上面的 resumeCancellableWith() 方法就算是恢復)。

createCoroutineUnintercepted()

這個方法其實就是對原來 launch() 方法傳遞過來的 Lambda 方法進行改造,我們直接看看,反編譯后的原來的 Lambda 對象:

image.png

其中我們發現它繼承于 SuspendLambda 對象,而 SuspendLambda 繼承于 ContinuationImpl 對象這個對象非常重要,是 Continuation 的一個實現。而當前的 Continuation 執行時的入口函數就是 invokeSuspend() 方法。后面我們會看到調用這個方法的邏輯。而它的構造函數中還有一個 Continuation 對象,這個其實就是上面的 StandalongCoroutine 對象,這個對象可以理解為父級的 Continuation 對象,在 Kotlin 源碼中通常被稱為 completion,這個就是表示當前 Continuation 執行完成后需要通知父級的 Continuation 繼續執行。

intercepted()

我們繼續看看 intercepted() 方法對我們原來的 Continuation 做了什么處理:

@SinceKotlin("1.3")  
public actual fun <T> Continuation<T>.intercepted(): Continuation<T> =  
(this as? ContinuationImpl)?.intercepted() ?: this

調用了 ContinuationImplintercepted() 方法,我們繼續追蹤:

    public fun intercepted(): Continuation<Any?> =
        intercepted
            ?: (context[ContinuationInterceptor]?.interceptContinuation(this) ?: this)
                .also { intercepted = it }

繼續調用 ContinuationIntercetor#interceptContinuation() 方法,我們之前設置的 CoroutineInterceptorDispatchers.Default。 我們來看看 CoroutineDispatcher#interceptContinuation() 方法的實現:

public final override fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T> =  
DispatchedContinuation(this, continuation)

將原來的 DispatherContinuation 作為參數構建了一個新的 DispatchedContinuation 對象。

resumeCancellableWith()

上面我們也講到它就相當于 resume 協程,他也是我們第一次 resume 協程。我們來看看它的實現:

@InternalCoroutinesApi
public fun <T> Continuation<T>.resumeCancellableWith(
    result: Result<T>,
    onCancellation: ((cause: Throwable) -> Unit)? = null
): Unit = when (this) {
    is DispatchedContinuation -> resumeCancellableWith(result, onCancellation)
    else -> resumeWith(result)
}

由于我們在 intercetped() 方法中將原來的 Continuation 對象,轉換成了 DispatchedContinuation 對象,所以我們這里調用的是 DispatchedContinuation#resumeCancellableWith() 方法,我們看看它的實現:

    @Suppress("NOTHING_TO_INLINE")
    internal 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
            dispatcher.dispatch(context, this)
        } else {
            executeUnconfined(state, MODE_CANCELLABLE) {
                if (!resumeCancelled(state)) {
                    resumeUndispatchedWith(result)
                }
            }
        }
    }
    
    @Suppress("NOTHING_TO_INLINE")
    internal inline fun resumeUndispatchedWith(result: Result<T>) {
        withContinuationContext(continuation, countOrElement) {
            continuation.resumeWith(result)
        }
    }

如果 Dispatcher#isDispatchNeeded() 返回 true 就表示可以使用 Dispatcher 來處理任務,也就是可以做到后續的任務最終在 Dispatcher 中的線程池中執行,通過 Dispatcher#dispatch() 方法下發任務,最后執行時是執行 run() 方法,實現是 DispatchedTask#run() 方法。反之就直接在當前線程調用 Continuation#resultWith() 方法。看看 DispatchedTask#run() 方法的實現:

    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>
            val continuation = delegate.continuation
            withContinuationContext(continuation, delegate.countOrElement) {
                // ...
                if (job != null && !job.isActive) {
                    // ...
                } else {
                    if (exception != null) {
                        continuation.resumeWithException(exception)
                    } else {
                        continuation.resume(getSuccessfulResult(state))
                    }
                }
            }
        } catch (e: Throwable) {
            // ...
        } finally {
            // ...
        }
    }

run() 方法執行過程中會判斷協程是否報錯了,如果沒有報錯直接執行 Continuation#result() 方法,如果有錯調用 Continuation#resumeWithException() 方法,上面說到這個 Continuation 在這里的實現類是 Lambada 對象繼承 SuspendLambda 對象實現的,這個 resume() 方法最終是由 BaseContinuationImpl#resumeWith() 方法實現的,這個方法可以說是 Kotlin 協程的靈魂,我們來看看它的實現,注意理解:

    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
            // 通知 debug 協程已經 resume
            probeCoroutineResumed(current)
            with(current) {
                // 父級的 continuation
                val completion = completion!! // fail fast when trying to resume continuation without completion
                val outcome: Result<Any?> =
                    try {
                        // 調用 invokeSuspend 入口函數
                        val outcome = invokeSuspend(param)
                        // 如果返回 COROUTINE_SUSPENDED 就表示掛起,同時退出循環
                        if (outcome === COROUTINE_SUSPENDED) return
                        // 如果返回不是 COROUTINE_SUSPENDED 就表示該 Continuation 方法已經執行完成了,需要通知它的父級的 Continuation,然后父級的 Continuation 繼續執行。
                        Result.success(outcome)
                    } catch (exception: Throwable) {
                        Result.failure(exception)
                    }
                releaseIntercepted() // this state machine instance is terminating
                if (completion is BaseContinuationImpl) {
                    // unrolling recursion via loop
                    // 進入下次循環,調用父級的 Continuation 的 invokeSuspend() 方法
                    current = completion
                    param = outcome
                } else {
                    // top-level completion reached -- invoke and return
                    // 恢復頂級的 Continuation,我們的代碼中是 StandaloneCoroutine
                    completion.resumeWith(outcome)
                    return
                }
            }
        }
    }

Continuation 中每調用一次 Kotlin 中的 suspend 的方法,Continuation 都會調用一次它的 invokeSuspend() 方法,當然每次調用 invokeSuspend() 方法執行的代碼都不一樣,Continuation 會通過一個 label 來記錄 invokedSuspend() 執行的位置,后面我們會看到這部分代碼,簡單再描述一下上面的代碼:

  1. 通過 probeCoroutineResumed() 方法通知協程進入 resume 狀態。
  2. 調用 invokeSuspend() 方法進入協程方法的具體執行,然后判斷返回值,如果返回值是 COROUTINE_SUSPENDED 就表示當前協程需要 suspend(也就是在執行一個異步任務,等待后續異步任務成功后再調用 resumeWith() 方法 resume。);如果返回值不是 COROUTINE_SUSPENDED 就表示當前的 Continuation 已經執行完成了。
  3. 當前 Continuation 執行完成了后,就需要在下次循環中調用父級的 ContinuationinvokeSuspend() 方法,直到頂級的 Continuation 執行完,在我們這里是 StandaloneCoroutine,注意理解這個循環調用。

我們站在初次啟動協程的邏輯來看看 resumeWith() 這個方法,初次調用 resumeWith() 時,對應的 Continuation 就是 SuspendContinuation,而最終的實現是由我們 launch 時傳遞進去的 Lambda 對象生成的,而父級的 Continuation 就是 StandaloneCoroutineSuspendContinuation 執行完成后就會調用 StandaloneCoroutineresumeWith() 方法,這也就標志當前的協程已經執行完成。

由于在 Kotlin 協程相關代碼編譯過程中會生成多個類似于 Lambda 對象那樣的匿名對象,不利于我們后續的源碼分析,我把 demo 中的編譯后的代碼反編譯后再重新整理命名,方便后續的代碼的分析。

  • MainActivity
public final class MainActivity extends AppCompatActivity {
    @Override // androidx.fragment.app.FragmentActivity, androidx.activity.ComponentActivity, androidx.core.app.ComponentActivity, android.app.Activity
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        BuildersKt__Builders_commonKt.launch$default(CoroutineScopeKt.CoroutineScope(Dispatchers.getDefault()), null, null, new LaunchContinuation(this, null), 3, null);
    }

    public final java.lang.Object helloWorld(kotlin.coroutines.Continuation<? super java.lang.String> continuation) {
        HelloWorldContinuation helloWorldContinuation = null;
        if (!(continuation instanceof HelloWorldContinuation)) {
            helloWorldContinuation = new HelloWorldContinuation(this, continuation);
        } else {
            helloWorldContinuation = (HelloWorldContinuation) continuation;
        }
        Object suspend = IntrinsicsKt.getCOROUTINE_SUSPENDED();
        switch (helloWorldContinuation.label) {
            case 0: {
                kotlin.ResultKt.throwOnFailure(helloWorldContinuation.result);
                StringBuilder stringBuilder = new StringBuilder();
                helloWorldContinuation.param1 = this;
                helloWorldContinuation.param2 = stringBuilder;
                helloWorldContinuation.label = 1;
                Object result = hello(helloWorldContinuation);
                if (result == suspend) {
                    return suspend;
                } else {
                    // 我們的代碼中不會有這種情況
                }
            }
            case 1: {
                kotlin.ResultKt.throwOnFailure(helloWorldContinuation.result);
                MainActivity mainActivity = (MainActivity) helloWorldContinuation.param1;
                StringBuilder stringBuilder = (StringBuilder) helloWorldContinuation.param2;
                String lastResult = (String) helloWorldContinuation.result;
                stringBuilder.append(lastResult);
                helloWorldContinuation.param1 = stringBuilder;
                helloWorldContinuation.param2 = null;
                helloWorldContinuation.label = 2;
                Object result = world(helloWorldContinuation);
                if (result == suspend) {
                    return suspend;
                } else {
                    // 我們的代碼中不會有這種情況
                }
            }
            case 2: {
                kotlin.ResultKt.throwOnFailure(helloWorldContinuation.result);
                StringBuilder stringBuilder = (StringBuilder) helloWorldContinuation.param1;
                String lastResult = (String) helloWorldContinuation.result;
                stringBuilder.append(lastResult);
                return stringBuilder.toString();
            }
            default: {

            }
        }

        throw new UnsupportedOperationException("Method not decompiled: com.tans.coroutine_test.MainActivity.helloWorld(kotlin.coroutines.Continuation):java.lang.Object");
    }

    public final Object hello(Continuation<? super String> continuation) {
        return delaSuspend(500L, "Hello, ", continuation);
    }

    public final Object world(Continuation<? super String> continuation) {
        return delaSuspend(500L, "World!!", continuation);
    }

    public final <T> Object delaSuspend(long time, T t, Continuation<? super T> continuation) {
        CancellableContinuationImpl cancellable$iv = new CancellableContinuationImpl(IntrinsicsKt.intercepted(continuation), 1);
        cancellable$iv.initCancellability();
        DelayKt.delay(time, new MainActivity$delaSuspend$2$1(cancellable$iv, t));
        Object result = cancellable$iv.getResult();
        if (result == IntrinsicsKt.getCOROUTINE_SUSPENDED()) {
            DebugProbesKt.probeCoroutineSuspended(continuation);
        }
        return result;
    }
}

  • LaunchContinuation

LaunchContinuation 就是由 launch() 方法的 Lambda 對象生成的 Continuation 對象,它本來是一個無規則的對象名字,為了代碼好閱讀我把它的名字修改成了 LaunchContinuation

final class LaunchContinuation extends SuspendLambda implements Function2<CoroutineScope, Continuation<? super Unit>, Object> {
    int label;
    final MainActivity mainActivity;

    public LaunchContinuation(MainActivity mainActivity, Continuation<? super LaunchContinuation> continuation) {
        super(2, continuation);
        this.mainActivity = mainActivity;
    }

    @Override
    public final Continuation<Unit> create(Object obj, Continuation<?> continuation) {
        return new LaunchContinuation(this.mainActivity, continuation);
    }

    public final Object invoke(CoroutineScope coroutineScope, Continuation<? super Unit> continuation) {
        return ((LaunchContinuation) create(coroutineScope, continuation)).invokeSuspend(Unit.INSTANCE);
    }

    @Override
    public final Object invokeSuspend(Object $result) {
        Object coroutine_suspended = IntrinsicsKt.getCOROUTINE_SUSPENDED();
        switch (this.label) {
            case 0:
                ResultKt.throwOnFailure($result);
                this.label = 1;
                Object helloWorld = this.mainActivity.helloWorld(this);
                if (helloWorld != coroutine_suspended) {
                    $result = helloWorld;
                    break;
                } else {
                    return coroutine_suspended;
                }
            case 1:
                ResultKt.throwOnFailure($result);
                break;
            default:
                throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
        }
        System.out.println($result);
        return Unit.INSTANCE;
    }
}

  • HelloWorldContinuation

HelloWroldContinuation 是由 helloWorld() 函數生成的一個 Continuation 對象,它本來也是一個匿名對象,為了方便源碼閱讀,我修改了該類的命名。

public final class HelloWorldContinuation extends ContinuationImpl {
    public Object param1;
    public Object param2;
    public int label;
    public Object result;
    final MainActivity mainActivity;

    public HelloWorldContinuation(MainActivity mainActivity, Continuation<? super HelloWorldContinuation> continuation) {
        super(continuation);
        this.mainActivity = mainActivity;
    }

    @Override
    public final Object invokeSuspend(Object obj) {
        this.result = obj;
        this.label |= Integer.MIN_VALUE;
        return this.mainActivity.helloWorld(this);
    }
}

  • DelayKt
public final class DelayKt {
    private static final Lazy delayExecutor$delegate = LazyKt.lazy(DelayKt$delayExecutor$2.INSTANCE);

    public static final ScheduledThreadPoolExecutor getDelayExecutor() {
        return (ScheduledThreadPoolExecutor) delayExecutor$delegate.getValue();
    }

    public static final void delay(long time, final Function0<Unit> callback) {
        Intrinsics.checkNotNullParameter(callback, "callback");
        getDelayExecutor().schedule(new Runnable() { // from class: com.tans.coroutine_test.DelayKt$$ExternalSyntheticLambda0
            @Override // java.lang.Runnable
            public final void run() {
                DelayKt.delay$lambda$0(Function0.this);
            }
        }, RangesKt.coerceAtLeast(time, 0L), TimeUnit.MILLISECONDS);
    }

    /* JADX INFO: Access modifiers changed from: private */
    public static final void delay$lambda$0(Function0 callback) {
        Intrinsics.checkNotNullParameter(callback, "$callback");
        callback.invoke();
    }
}

  • DelayKt$delayExecutor$2
    這是 DelayExecutor 的代理對象
final class DelayKt$delayExecutor$2 extends Lambda implements Function0<ScheduledThreadPoolExecutor> {
    public static final DelayKt$delayExecutor$2 INSTANCE = new DelayKt$delayExecutor$2();

    DelayKt$delayExecutor$2() {
        super(0);
    }

    @Override
    public final ScheduledThreadPoolExecutor invoke() {
        return new ScheduledThreadPoolExecutor(1);
    }
}

  • MainActivity$delaSuspend$2$1
    這是 MainActivity 中調用 delay() 方法時的 Lambda 生成的對象:
final class MainActivity$delaSuspend$2$1 extends Lambda implements Function0<Unit> {
    final CancellableContinuation<T> $cont;
    final T $data;
    public MainActivity$delaSuspend$2$1(CancellableContinuation<? super T> cancellableContinuation, T t) {
        super(0);
        this.$cont = cancellableContinuation;
        this.$data = t;
    }

    @Override // kotlin.jvm.functions.Function0
    /* renamed from: invoke  reason: avoid collision after fix types in other method */
    public final void invoke2() {
        Continuation continuation = this.$cont;
        Result.Companion companion = Result.Companion;
        continuation.resumeWith(Result.m122constructorimpl(this.$data));
    }
}

LaunchContinuation 的執行流程

上一小節講到協程啟動執行開始的方法是調用 launch() 方法中 Lambda 生成的 ContinuationresumeWith() 方法,這個 Continuation 我們把它命名成 LaunchContinuation (實際上是一個和 Lambda 對象一樣的匿名對象,對象名不易閱讀),而 resumeWith() 方法最終會調用 LaunchContinuation#invokeSuspend() 方法(忘記了的同學看看前面分析 BaseContinuationImpl#resumeWith() 代碼的部分),我們來看看 invokeSuspend() 方法的實現:

    public final Object invokeSuspend(Object $result) {
        Object coroutine_suspended = IntrinsicsKt.getCOROUTINE_SUSPENDED();
        switch (this.label) {
            case 0:
                // 檢查是否有異常
                ResultKt.throwOnFailure($result);
                // 修改 label 為 1
                this.label = 1;
                // 調用 helloWrold 方法,注意這里把自己當參數傳遞了過去
                Object helloWorld = this.mainActivity.helloWorld(this);
                if (helloWorld != coroutine_suspended) {
                    // 如果返回值不等于 coroutine_suspended 表示已經得到正確的返回結果,我們的例子不會執行這里
                    $result = helloWorld;
                    break;
                } else {
                    // 表示協程進入 suspend 狀態,等待下次調用 resumeWith() 方法繼續執行
                    return coroutine_suspended;
                }
            case 1:
                // 第二次執行 resuemWith() 方法,表示已經獲取到 helloWorld() 中的返回值,檢查返回中是否有異常。
                ResultKt.throwOnFailure($result);
                break;
            default:
                throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
        }
        // 執行打印最后的結果
        System.out.println($result);
        return Unit.INSTANCE;
    }

我們來分析一下上面的代碼:

  1. 記錄 Continuation 的執行位置的對象是 label,默認 label 的值為 0。
  2. label 為 0 的邏輯:
  • 判斷 result 中是否有異常
  • 修改 label 為 1,表示下次 resumeWith() 方法執行時,就是 case 1 那部分的邏輯。
  • 調用 MainActivity#helloWorld() 方法,注意這里將當前的 Continuation 對象傳遞給了 helloWorld() 方法。
  • 這里會判斷 helloWorld() 方法的返回值,如果返回值不為 COROUTINE_SUSPEND,就表示已經拿到返回值,不需要進入 suspend 狀態,然后進入 label 為 1 的邏輯(我們的代碼不會返回 COROUTINE_SUSPEND);反之就表示沒有獲取到返回值,需要掛起,直接返回 COROUTINE_SUSPEND,等待下次執行 resumeWith() 方法恢復,那時也就表示已經獲取到 helloWorld() 方法的返回值。
  1. label 為 1 的邏輯:
  • 判斷 result 中是否有異常。
  • 執行打印 result 的結果。
  • 這里返回了一個 Unit 而不是 COROUTINE_SUSPEND,就表示當前方法執行完畢了,我們前面提到 LaunchContinuation 的父級 ContinuationStandalongCoroutine,在講 BaseContinuationImpl#resumeWith() 方法時講過,當前 Continuation 執行完畢后就會執行它的父級 Continuation,也就是后續會執行 StandalongCoroutine#resumeWith() 方法,也就是通知 StandalongCoroutine,協程已經執行完畢了。

helloWorld() 的執行流程

LaunchContinuation 的執行過程中會調用 MainActivity#helloWorld() 方法,我們再來看看 MainActivity#helloWorld() 方法是如何處理的:

    public final java.lang.Object helloWorld(kotlin.coroutines.Continuation<? super java.lang.String> continuation) {
        HelloWorldContinuation helloWorldContinuation = null;
        if (!(continuation instanceof HelloWorldContinuation)) {
            // 構建一個 HelloWorldContinuation 實例,注意這里將 Launch Continuation 作為 HelloWorldContinuation 的父級 Continuation
            helloWorldContinuation = new HelloWorldContinuation(this, continuation);
        } else {
            helloWorldContinuation = (HelloWorldContinuation) continuation;
        }
        Object suspend = IntrinsicsKt.getCOROUTINE_SUSPENDED();
        switch (helloWorldContinuation.label) {
            case 0: {
                // 檢查 result 是否有異常 
                kotlin.ResultKt.throwOnFailure(helloWorldContinuation.result);
                // 構建 StringBuilder 對象
                StringBuilder stringBuilder = new StringBuilder();
                // 將 MainActivity 實例賦值給 HelloWorldContinuation#param1
                helloWorldContinuation.param1 = this;
                // 將 StringBuilder 實例賦值給 HelloWorldContinuation#param2
                helloWorldContinuation.param2 = stringBuilder;
                // 修改 label 為 1
                helloWorldContinuation.label = 1;
                // 執行 hello() 方法
                Object result = hello(helloWorldContinuation);
                // 返回值為 COROUTINE_SUSPEND 表示當前協程變為 suspend 狀態
                if (result == suspend) {
                    return suspend;
                } else {
                    // 我們的代碼中不會有這種情況
                }
            }
            case 1: {
                // 檢查 result 是否有異常 
                kotlin.ResultKt.throwOnFailure(helloWorldContinuation.result);
                MainActivity mainActivity = (MainActivity) helloWorldContinuation.param1;
                // 獲取參數 StringBuilder
                StringBuilder stringBuilder = (StringBuilder) helloWorldContinuation.param2;
                // 獲取上次的 hello() 方法的返回結果
                String lastResult = (String) helloWorldContinuation.result;
                // 將 hello() 方法的返回結果添加到 StringBuilder 中
                stringBuilder.append(lastResult);
                // 將 StringBuilder 賦值給 HelloWorldContinuation#param1
                helloWorldContinuation.param1 = stringBuilder;
                helloWorldContinuation.param2 = null;
                // 修改 label 為 2
                helloWorldContinuation.label = 2;
                // 執行 world() 方法。
                Object result = world(helloWorldContinuation);
                // 返回值為 COROUTINE_SUSPEND 表示當前協程變為 suspend 狀態
                if (result == suspend) {
                    return suspend;
                } else {
                    // 我們的代碼中不會有這種情況
                }
            }
            case 2: {
            // // 檢查 result 是否有異常 
                kotlin.ResultKt.throwOnFailure(helloWorldContinuation.result);
                // 獲取 StringBuilder
                StringBuilder stringBuilder = (StringBuilder) helloWorldContinuation.param1;
                // 獲取 world() 方法的返回值
                String lastResult = (String) helloWorldContinuation.result;
                // 將 world() 方法的返回值,添加到 StringBuilder 中
                stringBuilder.append(lastResult);
                // 將最終結果返回,也就表示當前 Continuation 執行完畢
                return stringBuilder.toString();
            }
            default: {

            }
        }

        throw new UnsupportedOperationException("Method not decompiled: com.tans.coroutine_test.MainActivity.helloWorld(kotlin.coroutines.Continuation):java.lang.Object");
    }

我再講Retrofit 的源碼的文章中也講過 Kotlinsuspend 函數在編譯處理后,會添加一個 Continuation 對象參數,然后返回值變成 Object,在 helloWorld() 方法中也得到了印證。
這個邏輯比 LaunchContinuation 中的狀態稍微多了一些,然后各種參數也復雜一點,我們來分析一下:

  1. 如果 continuation 參數不是 HelloWorldContinuation (這種情況就是 LaunchContinuation),構建一個 HelloWorldContinuation 實例,它的父 ContinuationLaunchContinuation
  2. label 為 0 的邏輯:
  • 檢查 result 中是否有異常。
  • 構建一個新的 StringBuilder 對象賦值給 HelloWorldContinuation#param2
  • 修改 label 為 1。
  • 執行 hello() 方法,如果返回值是 COROUTINE_SUSPEND 表示協程進入 suspend 狀態,我們的代碼一定會到這段邏輯;反之就直接執行 label 為 1 的邏輯。
  1. label 為 1 的邏輯:
  • 檢查 result 中是否有異常。
  • HelloWorldContinaution#param2 中獲取 StringBuilder 實例。
  • HelloWorldContinuation#result 中獲取 hello() 方法的返回值。
  • hello() 方法的返回結果寫入到 StringBuilder 中。
  • 構建一個新的 StringBuilder 對象賦值給 HelloWorldContinuation#param1
  • 修改 label 為 2,執行 world() 方法,和執行 hello() 方法一樣,在我們的代碼中一定會進入 suspend 狀態。
  1. label 為 2 的邏輯:
  • 檢查 result 中是否有異常。
  • HelloWorldContinaution#param1 中獲取 StringBuilder 實例。
  • HelloWorldContinuation#result 中獲取 world() 方法的返回值。
  • world() 方法的返回結果寫入到 StringBuilder 中。
  • 將最終結果返回,也就標志 HelloWorldContinuation 執行完成了。

我們再簡單看看 HelloWorldContinuation#invokeSuspend() 方法的實現:

    @Override
    public final Object invokeSuspend(Object obj) {
        this.result = obj;
        this.label |= Integer.MIN_VALUE;
        return this.mainActivity.helloWorld(this);
    }

代碼非常簡單,將上次的執行的結果寫入到 result 中,然后調用 helloWorld() 方法。

hello()world() 的執行流程

    public final Object hello(Continuation<? super String> continuation) {
        return delaSuspend(500L, "Hello, ", continuation);
    }

    public final Object world(Continuation<? super String> continuation) {
        return delaSuspend(500L, "World!!", continuation);
    }

    public final <T> Object delaSuspend(long time, T t, Continuation<? super T> continuation) {
        CancellableContinuationImpl cancellable$iv = new CancellableContinuationImpl(IntrinsicsKt.intercepted(continuation), 1);
        cancellable$iv.initCancellability();
        DelayKt.delay(time, new MainActivity$delaSuspend$2$1(cancellable$iv, t));
        Object result = cancellable$iv.getResult();
        if (result == IntrinsicsKt.getCOROUTINE_SUSPENDED()) {
            DebugProbesKt.probeCoroutineSuspended(continuation);
        }
        return result;
    }

hello()world() 方法都調用了 delaSuspend() 方法用來講 delay() 方法的異步 callback 調用轉換成一個協程 suspend 方法。源碼是這樣的:

    suspend fun <T> delaSuspend(time: Long, data: T): T {
        return suspendCancellableCoroutine { cont ->
            delay(time) {
                cont.resumeWith(Result.success(data))
            }
        }
    }

delaSuspend() 的處理中會創建一個 CancellableContinuationImpl 對象,它的父級 ContinuationHelloWorldContinuation,這里注意看調用了 IntrinsicsKt.intercepted() 方法來代理 HelloWorldContinuation,前面我有講過這個方法,它會讓后續的 resumeWith() 方法在 Dispacher 中對應的線程中執行。調用了 DelayKt.delay() 方法然后傳入了一個 Lambda 對象,我們看看 Lambda 對象中的方法執行:

    public final void invoke2() {
        Continuation continuation = this.$cont;
        Result.Companion companion = Result.Companion;
        continuation.resumeWith(Result.m122constructorimpl(this.$data));
    }

簡單且高效,在回調成功時,直接調用 CancellableContinuationImpl#resumeWith() 方法使協程進入 resume 狀態,后續的邏輯的執行的線程由 Dispatcher 決定。

最后

看到這里你可能還是有點懵的狀態,這是非常正常的,我再來完整的理一下整個流程:

CoroutineScope#launch() 方法中會創建一個 StandaloneCoroutine 對象,然后通過 launch() 方法傳過來的 Lambda 對象構建一個 LaunchContinuation 對象它的父級 ContinuationStandaloneCoroutine,然后調用 LaunchContinuation#resumeWith() 方法標志協程開始。
LaunchContinuation 第一次執行 resumeWith() 時,會調用 helloWorld() 方法,這里會 HelloWorldContinuation 對象,它的父級 ContinuationLaunchContinuation 對象。第一次執行 helloWorld() 方法時會調用 hello() 方法,在 hello() 方法中會構建一個 CancellableContinuationImpl 對象,它的父級 ContinuationHelloWorldContinuation,當 hello() 方法的 callback 異步調用成功后會調用 CancellableContinuationImpl#resumeWith() 方法 resume 協程,最終會調用到 HelloWorldContinuation#resumeWith() 方法中去,這里也會觸發 helloWorld() 方法的第二次執行,在第二次執行的過程中會調用 world() 方法,world() 方法的處理邏輯和 hello() 方法一模一樣,回調完成后就會觸發第三次調用 helloWorld() 方法,第三次調用的時候會組合 hello()world() 兩次方法的結果得到最終的結果,然后返回,這時 HelloWorldContinuation 就會調用它的父級的 Continuation 中的 resumeWith() 方法,也就是 LaunchContinuation#resumeWith() 方法,用來通知 LaunchContinuation 表示 helloWorld() 方法已經執行完畢,這個時候是第二次執行 LaunchContinuation#resumeWith(),這時他也不用再進入 suspend 狀態,又會繼續調用它的父級 ContinuationresumeWith() 方法,也就是 StandaloneConroutine#resumeWith() 的方法,它的這個方法調用后也就標志這個協程執行完畢了。

如果到這里還是沒有理解這個過程,推薦你再多看幾遍,一定能夠看懂的,其實就是通過 Continuation 來處理 callback 的套娃操作,當理解了這個過程后,協程的很多地方的源碼你就能夠看得懂了。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容