text
We often use launch, let’s see what it is today.
Suggestions: Remember to eat before eating this articleKotlin coroutine createCoroutine and startCoroutine
Use launch
We should be very familiar with launch, just give a simple example:
fun main() { val coroutineScope = CoroutineScope(Job()) { println("In 1969, Ye Wenjie entered the Red Bank Base") println("In 1971, Ye Wenjie sent a signal to the sun for the first time at the Hongban base, but no echoes were found") delay(4000L) println("In 1975, the three-body world learned about the existence of the earth") } (5000L) }
Sleep(5000L) and launch are inside 2 threads and do not interfere with each other
Simply use launch and delay to output several statements. In order to understand its underlying implementation principle, it is still the same rule, decompile it first.
public final class LaunchTestKt { public static final void main() { Job unused = BuildersKt__Builders_commonKt.launch$default(($default((Job) null, 1, (Object) null)), (CoroutineContext) null, (CoroutineStart) null, new LaunchTestKt$main$1((Continuation<? super LaunchTestKt$main$1>) null), 3, (Object) null); (5000); } } final class LaunchTestKt$main$1 extends SuspendLambda implements Function2<CoroutineScope, Continuation<? super Unit>, Object> { int label; LaunchTestKt$main$1(Continuation<? super LaunchTestKt$main$1> continuation) { super(2, continuation); } public final Continuation<Unit> create(Object obj, Continuation<?> continuation) { return new LaunchTestKt$main$1(continuation); } public final Object invoke(CoroutineScope coroutineScope, Continuation<? super Unit> continuation) { return ((LaunchTestKt$main$1) create(coroutineScope, continuation)).invokeSuspend(); } public final Object invokeSuspend(Object $result) { Object coroutine_suspended = IntrinsicsKt.getCOROUTINE_SUSPENDED(); switch () { case 0: ($result); ("In 1969, Ye Wenjie entered the Red Bank Base"); ("In 1971, Ye Wenjie sent a signal to the sun for the first time at the Hongban base, but no echoes were found"); = 1; if ((4000, this) != coroutine_suspended) { break; } else { return coroutine_suspended; } case 1: ($result); break; default: throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine"); } ("In 1975, the three-body world learned about the existence of the earth"); return ; } }
ps: The above code is obtained by decompiling apk by jadx, which looks more humane. The specific process is to use Android Studio to write a demo of the suspend function, then compile it into apk, then decompile the apk with jadx, and get the corresponding class's decompilation Java source code. I feel that the source code obtained in this way is slightly easier to understand than the source code obtained directly through Android Studio's Tools->Kotlin->Show Kotlin.
Hey,LaunchTestKt$main$1
Does it look familiar? Isn't this the anonymous internal class we obtained when we analyzed the startCoroutine principle, it is exactly the same. The LaunchTestKt$main$1 class corresponds to the launch Lambda block, which is essentially a Continuation.
startCoroutine principle
LaunchTestKt$main$1
The relevant principles have been analyzed before and will not be discussed here. Here we mainly look at how launch is related to thisLaunchTestKt$main$1
Make association.
Launch principle
The launch function is as follows:
public fun ( context: CoroutineContext = EmptyCoroutineContext, start: CoroutineStart = , block: suspend CoroutineScope.() -> Unit ): Job { //Code 1 val newContext = newCoroutineContext(context) //Code 2 val coroutine = if () LazyStandaloneCoroutine(newContext, block) else StandaloneCoroutine(newContext, active = true) //Code 3 (start, coroutine, block) return coroutine }
- Construct the incoming CoroutineContext to a new context
- Start mode, determine whether it is lazy loading. If it is lazy loading, build a lazy load coroutine object, otherwise it is standard.
- Start coroutine
Let's not look at the context-related content for now, because our demo is not lazy to load, so the StandaloneCoroutine is created. Let's take a look at how start starts the coroutine.
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 } } public abstract class AbstractCoroutine<in T>( parentContext: CoroutineContext, initParentJob: Boolean, active: Boolean ) : JobSupport(active), Job, Continuation<T>, CoroutineScope { public fun <R> start(start: CoroutineStart, receiver: R, block: suspend R.() -> T) { start(block, receiver, this) } }
The start function is implemented in the parent class AbstractCoroutine. A new start function is called in this start function. When we click on the start function in this and want to go in and look at the source code, we find that we can't click it, and after clicking it, it is still at the current location...? ? ? What's the situation
Find invoke method in CoroutineStart
After careful observation, I found that start is a CoroutineStart object. I directly use the CoroutineStart object and then follow it with brackets. This is the class that defines the operator invoke method, and Kotlin can simplify the call in this way. Let's go directly to CoroutineStart to find the invoke method:
public enum class CoroutineStart { DEFAULT, LAZY, ATOMIC, UNDISPATCHED; public operator fun <R, T> invoke(block: suspend R.() -> T, receiver: R, completion: Continuation<T>): Unit = when (this) { DEFAULT -> (receiver, completion) ATOMIC -> (receiver, completion) UNDISPATCHED -> (receiver, completion) LAZY -> Unit // will start lazily } public val isLazy: Boolean get() = this === LAZY }
CoroutineStart is an enumeration class that defines several ways of starting coroutines: DEFAULT, LAZY, ATOMIC, UNDISPATCHED. In the invoke function, the coroutine is enabled according to which startup method is currently launched.
When using ATOMIC, that is, a coroutine that cannot be cancelled, it will be triggered(receiver, completion)
. Do you think it looks familiar? It is actually what we analyzed in the last classThe key to starting coroutine: startCoroutine。
The default method used in the demo is DEFAULT. It just wraps the startCoroutine based on ATOMIC to make it a coroutine that can be responsively cancelled. The UNDISPATCHED method is that it is not distributed to other threads for execution, but is directly executed in the current thread.
startCoroutineCancellable logic
Let’s take a look at the startCoroutineCancellable logic that follows DEFAULT:
public fun <T> (suspend () -> T).startCoroutineCancellable(completion: Continuation<T>): Unit = runSafely(completion) { createCoroutineUnintercepted(completion).intercepted().resumeCancellableWith((Unit)) } public actual fun <T> (suspend () -> T).createCoroutineUnintercepted( completion: Continuation<T> ): Continuation<Unit> { val probeCompletion = probeCoroutineCreated(completion) return if (this is BaseContinuationImpl) //Go here create(probeCompletion) else createCoroutineFromSuspendFunction(probeCompletion) { (this as Function1<Continuation<T>, Any?>).invoke(it) } }
This is the code analyzed in the previous article, and even if the launch is finished, it will be completed. In essence, it encapsulates the basic API of startCoroutine() to make it more convenient for developers to use.
summary
Launch, async, etc. are the intermediate layers in the Kotlin coroutine framework, which are coroutine builders. And inside the coroutine builder, it is actually the coroutine basic API:createCoroutine{}
、startCoroutine{}
package. In addition to having the basic ability to start coroutines, they also support the introduction of parameters such as CoroutineContext (structured concurrency), CoroutineStart (start mode), etc., which is convenient for developers to use.
The above is the detailed explanation of the principle of Kotlin coroutine launch. For more information about Kotlin coroutine launch, please pay attention to my other related articles!