1. Simply start coroutine Demo

This article example code address: gitee.com/mcaotuman/k…

Write a small demo, remember how to use first, then analyze the source code.

Three suspend functions:

Output result:

2. Build the suspend decorated lambda function

Suspend 2.1 lambda

Here is code suspend to modify a lambda expression:

val mySuspendLambda: Suspend () -> String = {// Returns a String hello world val one = commonSuspendFun() // returns a String Hello world2 val two = CommonSuspendFun2 () // Return a string hello world3 val three = commonSuspendFun3() one+two+three}Copy the code

What does suspend lambda decomcompile into Java code look like?

Let’s use the JEB tool to decompile:

We locate the package three according to the code

Right-click to parse the bytecode file and get the decomcompiled Java code:

SuspendLambda class (very important) : suspend lambda class (SuspendLambda class)

Now that you see the BaseContinuationImpl class and the Continuation interface, you might be confused. That’s okay. Let’s move on.

2.2 suspend function

Here is the suspend function:

suspend fun commonSuspendFun(): String {
    return "[hello world] "
}
Copy the code

Decompilating to Java code looks like this:

Instead of the suspend keyword, there is a Continuation type parameter.

The Continuation appears again, as mentioned in the previous section that SuspendLambda implements the Continuation interface

What exactly is a Continuation?

Here, take a look at the source code:

Isn’t this Continuation interface something like CallBack? Isn’t the resumeWith method for continuations the equivalent of onSuccess for CallBack? That’s right.

This process of converting a suspended function to a CallBack function is called a continuation-passing Style Transformation.

I don’t know if you noticed, but instead of returning String, Object!

Because the suspend modified function, can return to CoroutineSingletons. COROUTINE_SUSPENDED, may also return to actual results [hello world], and may even return null, in order to fit all the possibilities, The return type of the CPS function can only be Object.

3. Create the coroutine createCoroutine

How am I looking at the Kotlin Expect keyword implementation (actual) on the corresponding platform? This article has also mentioned createCoroutine source, he finally will be transferred to the createCoroutineUnintercepted method.

From the above analysis, we know that suspend lambda becomes SuspendLambda after the compiler’s dark magic compilation, which in effect inherits BaseContinuationImpl, so it goes directly to the Create method.

Actually, go to the constructor of the SuspendLambda class

Finally, go to the BaseContinuationImpl and build the SuspendLambda.

4. Start coroutine: Resume

As you know from Step 3, we have an instance of SuspendLambda, which we now callresumeExtension functions start coroutines.

It’s actually the resumeWith method that calls the Continuation

So let’s debug the resumeWith method of BaseContinuationImpl to see how it starts the coroutine.

CoroutineRunKt$testFunGetContinuation$mySuspendLambda$1 corresponds to the suspend lambda object

MySuspendLambda. The main purpose of this object is to maintain the state machine, and this is the key to kotlin’s coroutine. CoroutineRunKt$testFunGetContinuation$mySuspendLambda$1

Jeb demo version does not allow copy code, pain, only screenshots. So I typed the code manually and commented out the key steps:

public final Object invokeSuspend(Object arg8) { String v3; CoroutineRunKt.testFunGetContinuation.mySuspendLambda.1 this; / / where v0: hang function returns identifier SUSPEND_FLAG Object where v0 = IntrinsicsKt. GetCOROUTINE_SUSPENDED (); Switch (this.label) {case 0: result.throwonFailure (arg8); This. label = 1; // If SUSPEND_FLAG is returned, return SUSPEND_FLAG. // The suspension function returns to the invokeSuspend method when it calls resume with the Continuation passed in CoroutineRunKt.commonSuspendFun((Continuation)this); if (v2 == v0) { return v0; } this = this; // goto label_48; case 1: ResultKt.throwOnFailure(arg8); this = this; label_48: this.L$0 = one; This. label = 2; / / hang function called (5) and (3) of the same Object v3_1 = CoroutineRunKt.com monSuspendFun2 ((Continuation) this); if (v3_1 == v0) { return v0; } v3 = one; arg8 = v3_1; Case 2 label_60: String two = (String)arg8; this.L$0 = v3; this.L$1 = two; This. label = 3; / / (nine) call last hang up function And (3) the same as the result Object v4 = CoroutineRunKt.com monSuspendFun3 ((Continuation) this); Return v4 = v0? v0 : v3 +two +((String)v4); case 2: String v2_2 = (String) this.L$0; ResultKt.throwOnFailure(arg8); v3 = v2_2; this = this; // (7) goto label_60; case 3: Stirng v2_3 = (Stirng)this.L$1; Stirng v1 = (Stirng)this.L$0; ResultKt.throwOnFailure(arg8); return v1 + v2_3 +((Stirng)arg8); default: throw new IllgelStateException("call to 'resume' befor 'invoke' with coroutinue"); }}Copy the code

Is it a bit of a headache to look at bytecode decompiled code? I think so too. I wrote a Java version of the state machine implementation Demo to help you understand. Code address: gitee.com/mcaotuman/k…

The switch case expression implements the state machine mode of the coroutine. Continuation. label is the flag bit of the state machine. If a function is put up, the return value is: CoroutineSingletons. COROUTINE_SUSPENDED, function recovery after will continue the current label executive, until all the label is done.

4. At the end

Finally finished writing Kotlin coroutine start source analysis, in fact, CPS transformation and state machine understand, coroutine principle is also quite easy to accept. My level is limited, if there are mistakes or inadequacies in the article is unavoidable. Readers are expected to give criticism and correction. Blue Blue