An overview of the
The first contact coroutine, the content of the article if there are errors welcome to point out, common progress! Feel good to leave a like to go again ha ~
- Link to blog
- Basic use of Kotlin coroutines
- Kotlin coroutines in-depth understanding of coroutines work
- Coroutine cancellation and exception handling for Kotlin coroutines
State machine of coroutine
This chapter examines the process of starting, suspending, and resuming coroutines as an example:
private suspend fun getId(a): String {
return GlobalScope.async(Dispatchers.IO) {
delay(1000)
"hearing"
}.await()
}
private suspend fun getAvatar(id: String): String {
return GlobalScope.async(Dispatchers.IO) {
delay(1000)
"avatar-$id"
}.await()
}
fun main(a) {
GlobalScope.launch {
val id = getId()
val avatar = getAvatar(id)
println("${Thread.currentThread().name} - $id - $avatar")}}Copy the code
In the main method above, the globalScope.launch coroutine body is suspended after it executes to getId and does not resume the launch coroutine until getId returns a usable result. The same goes for getAvatar. The coroutine internal implementation uses a state machine to handle the different hang points, decompiling the GlobalScope.launch coroutine body bytecode into Java code, which looks like this (with some cuts) :
BuildersKt.launch$default((CoroutineScope)GlobalScope.INSTANCE, (CoroutineContext)null,
(CoroutineStart)null, (Function2)(new Function2((Continuation)null) {
int label;
public final Object invokeSuspend(@NotNull Object $result) {
Object var10000;
String id;
label17: {
CoroutineScope $this$launch;
switch(this.label) {
case 0: // a
ResultKt.throwOnFailure($result);
$this$launch = this.p$;
this.label = 1; // Set the label to 1
var10000 = getId(this);
if (var10000 == COROUTINE_SUSPENDED) {
return COROUTINE_SUSPENDED;
}
// If there is already a result, then break the result without suspending it
break;
case 1: // b
ResultKt.throwOnFailure($result);
var10000 = $result;
break;
case 2: // d
id = (String)this.L$1;
ResultKt.throwOnFailure($result);
var10000 = $result;
break label17; / / exit label17
default:
throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
}
// c
id = (String)var10000;
this.L$1 = id; // Assign id to L$1
this.label = 2; // Set the label to 2
var10000 = getAvatar(id, this);
if (var10000 == COROUTINE_SUSPENDED) {
returnCOROUTINE_SUSPENDED; }}// e
String avatar = (String)var10000;
String var5 = var9.append(var10001.getName()).append("-").append(id).append("-").append(avatar).toString();
System.out.println(var5);
return Unit.INSTANCE;
}
@NotNull
public final Continuation create(@Nullable Object value, @NotNull Continuation completion) {
Intrinsics.checkParameterIsNotNull(completion, "completion");
Function2 var3 = new <anonymous constructor>(completion);
var3.p$ = (CoroutineScope)value;
return var3;
}
public final Object invoke(Object var1, Object var2) {
return ((<undefinedtype>)this.create(var1, (Continuation)var2)).invokeSuspend(Unit.INSTANCE); }}Copy the code
The invokeSuspend method is called after the results from the suspend function in the body of the coroutine. We’ll see where it is called later.
- a: If the return value of getId is COROUTINE_SUSPENDED, then the code following getId in the launch coroutine body will not be executed. That is, the launch coroutine body is suspended (non-blocking, the thread still does other work). Here, the label is set to 1. If the getId already has a result (no suspend function is called internally, such as delay), then the getId is not suspended, but directly breaks.
- b: If the getId returns COROUTINE_SUSPENDED in a, then the invokeSuspend method of the launch coroutine body will be re-executed once the getId result is returned. Label ==1 (id = 10000) ==1 (id = 10000)
- C: If you break the getId in a or get the getId in B, then you get the id and set the label to 2. Then the getAvatar method is called, which is similar to getId. If it returns COROUTINE_SUSPENDED, the coroutine is suspended until the next invokeSuspend is executed, otherwise it leaves Label17 and executes the subsequent logic.
- d: If the getAvatar returns COROUTINE_SUSPENDED, then the invokeSuspend method of the launch coroutine body is called again when the getAvatar result is available. At this point, according to the label==2 to come here and get the previous ID value, verify the result(i.e. Avatar), and then break the Label17.
- The suspend function in the launch coroutine body is completed after e: C directly returns the available result or D break Label17. The rest of the logic is executed here.
Suspend functions do not block the thread, and suspend coroutines are not necessarily suspended by suspend functions. If the result of the related call is already available, it continues to run without suspending. For example, await() suspend functions can return the result directly when the result of async{} return Deferred is already available. No more hanging coroutines.
This section takes a look at the code logic of decompiled the launch coroutine body into Java, and how and when the invokeSuspend is called, as described below.
Coroutine creation and launch
This section uses the default coroutinescope.launch {} as an example to see how Kotlin coroutines are created and launched from the source:
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
}
// AbstractCoroutine.kt
// receiver: StandaloneCoroutine
// block: suspend StandaloneCoroutine.() -> Unit
// private open class StandaloneCoroutine(...) : AbstractCoroutine<Unit>(...) {}
// public abstract class AbstractCoroutine<in T>(...) : JobSupport(active), Job, Continuation<T>, CoroutineScope {}
public fun <R> start(start: CoroutineStart, receiver: R, block: suspend R. () - >T) {
// Invoke the invoke method in CoroutineStart
start(block, receiver, this)}public enum class CoroutineStart {
// block - StandaloneCoroutine.() -> Unit
// receiver - StandaloneCoroutine
// completion - StandaloneCoroutine<Unit>
public operator fun <R, T> invoke(block: suspend R. () - >T, receiver: R, completion: Continuation<T>): Unit =
when (this) {
// Call different methods depending on the type of the start parameter
DEFAULT -> block.startCoroutineCancellable(receiver, completion)
ATOMIC -> block.startCoroutine(receiver, completion)
UNDISPATCHED -> block.startCoroutineUndispatched(receiver, completion)
LAZY -> Unit // will start lazily}}Copy the code
The next see startCoroutineCancellable method:
// receiver - StandaloneCoroutine
// completion - StandaloneCoroutine<Unit>
internal fun <R, T> (suspend (R) -> T).startCoroutineCancellable(receiver: R, completion: Continuation<T>) =
runSafely(completion) {
createCoroutineUnintercepted(receiver, completion).intercepted().resumeCancellableWith(Result.success(Unit))}Copy the code
CreateCoroutineUnintercepted method creates a Continuation type (coroutines) instance, creating a collaborators cheng:
public actual fun <R, T> (suspend R.() -> T).createCoroutineUnintercepted(
receiver: R, completion: Continuation<T>
): Continuation<Unit> {
return if (this is BaseContinuationImpl) create(receiver, completion) else // ...
}
Copy the code
Is invoked (suspend (R) – > T) createCoroutineUnintercepted method (suspend (R) – > T) is coroutines. Looking directly at the compiled bytecode of GlobalScope.launch in the above example code, you can see that the lambda expression passed into coroutInescope.launch is compiled as a subclass of SuspendLambda:
final class Main$mainThe $1extends kotlin/coroutines/jvm/internal/SuspendLambda implements kotlin/jvm/functions/Function2
Copy the code
Its inheritance relationship is: SuspendLambda -> ContinuationImpl -> BaseContinuationImpl -> Continuation, so follow the create(Receiver, completion) method, From the decompiled Java code above, you can see that the create method creates a Continuation instance, and take a look at the compiled bytecode of the Kotlin code (the package name has been omitted) :
public final create(Ljava/lang/Object; Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
// ...
NEW Main$main$1
Copy the code
As you can see above, the create method creates an instance of Main$Main$1, which inherits from SuspendLambda, so the Continuation created by the create method is a SuspendLambda object.
Namely createCoroutineUnintercepted method creates a SuspendLambda instance. Then take a look at intercepted:
public actual fun <T> Continuation<T>.intercepted(a): Continuation<T> =
// If ContinuationImpl, the intercepted method is called, otherwise it returns itself
ContinuationImpl // This is a subclass of Main$Main$1 instance -ContinuationImpl
(this as? ContinuationImpl)? .intercepted() ?:this
// ContinuationImpl
public fun intercepted(a): Continuation<Any? > =// Context [ContinuationInterceptor] is the CoroutineDispatcher instance
// Thread scheduling is required - return a DispatchedContinuation with a continuation value of SuspendLambda
// No thread scheduling required - return SuspendLambdaintercepted ? : (context[ContinuationInterceptor]? .interceptContinuation(this) ?: this).also { intercepted = it }
// CoroutineDispatcher
// continuation - SuspendLambda -> ContinuationImpl -> BaseContinuationImpl
public final override fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T> =
DispatchedContinuation(this, continuation)
Copy the code
Let’s take a look at how resumeCancellableWith starts the coroutine, which also involves the logic of Dispatchers thread scheduling:
internal class DispatchedContinuation<in T>(
@JvmField val dispatcher: CoroutineDispatcher,
@JvmField val continuation: Continuation<T>
) : DispatchedTask<T>(MODE_ATOMIC_DEFAULT), CoroutineStackFrame, Continuation<T> by continuation {
public fun <T> Continuation<T>.resumeCancellableWith(result: Result<T>): Unit = when (this) {
// The thread is scheduled, and finally executed to the continuation.resumeWith method
is DispatchedContinuation -> resumeCancellableWith(result)
// Execute the continuation.resumeWith method directly
else -> resumeWith(result)
}
inline fun resumeCancellableWith(result: Result<T>) {
val state = result.toState()
// Determine if thread scheduling is required
if (dispatcher.isDispatchNeeded(context)) {
_state = state
resumeMode = MODE_CANCELLABLE
// Do the scheduling first
dispatcher.dispatch(context, this)}else {
executeUnconfined(state, MODE_CANCELLABLE) {
if(! resumeCancelled()) {// Execute the coroutine directly in the current thread without scheduling
resumeUndispatchedWith(result)
}
}
}
}
inline fun resumeUndispatchedWith(result: Result<T>) {
withCoroutineContext(context, countOrElement) {
continuation.resumeWith(result)
}
}
}
Copy the code
- When need to thread scheduling, after scheduling will call DispatchedContinuation. Continuation. ResumeWith to start coroutines, continuation of them SuspendLambda instance;
- When thread scheduling is not required, suspendlambd.resumewith is called directly to start the coroutine.
The resumeWith method calls the resumeWith method in the parent BaseContinuationImpl:
internal abstract class BaseContinuationImpl(public valcompletion: Continuation<Any? >? : Continuation<Any? >, CoroutineStackFrame, Serializable {public final override fun resumeWith(result: Result<Any? >) {
// ...
val outcome = invokeSuspend(param)
// ...}}Copy the code
Coroutines, therefore, the start is through BaseContinuationImpl resumeWith method calls to subclass SuspendLambda. InvokeSuspend method, and then through the state machine to control the operation of the order.
Suspend and resume coroutines
The Kotlin compiler generates a subclass of SuspendLambda for the body of the coroutine, and the real operational logic for the coroutine is in its invokeSuspend method. In the previous section, we looked at how launch creates and starts coroutines. In this section, we’ll look at how coroutines are suspended when the coroutine code is executed to the suspend function, and how the suspend function resumes when the suspend function completes and has available results.
The internal implementation of the Kotlin coroutine uses some of the compilation techniques of the Kotlin compiler. When the Suspend function is called, an additional implicit argument is passed in. This argument is a Continuation type, which encapsulates the code logic to be executed after the coroutine Resume.
private suspend fun getId(a): String {
return GlobalScope.async(Dispatchers.IO) {
delay(1000)
"hearing"
}.await()
}
/ / Decompile into Java
final Object getId(@NotNull Continuation $completion) {
// ...
}
Copy the code
The $Completion parameter passed in, as you saw in the previous section, is the coroutine body object where the getId method was called, which is a SuspendLambda object. A Continuation is defined as follows:
public interface Continuation<in T> {
public val context: CoroutineContext
public fun resumeWith(result: Result<T>)
}
Copy the code
Decompilate the bytecode compiled by the getId method into Java code as follows (some of the code has been deleted and modified for easy reading) :
final Object getId(@NotNull Continuation $completion) {
// Create and start coroutines
return BuildersKt.async$default((CoroutineScope)GlobalScope.INSTANCE, (CoroutineContext)Dispatchers.getIO(), (CoroutineStart)null, (Function2)(new Function2((Continuation)null) {
int label;
@Nullable
public final Object invokeSuspend(@NotNull Object $result) {
switch(this.label) {
case 0:
ResultKt.throwOnFailure($result);
this.label = 1;
if (DelayKt.delay(1000L.this) == COROUTINE_SUSPENDED) {
return COROUTINE_SUSPENDED;
}
break;
case 1:
ResultKt.throwOnFailure($result);
break;
default:
throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
}
return "hearing";
}
// ...
}), 2, (Object)null).await($completion); // Call await() suspend function
}
Copy the code
In addition to the coroutine state machine section, when the launch coroutine body executes the getId method, it is suspended based on whether the return value is COROUTINE_SUSPENDED. Since the logic of getId is to start a new coroutine with async, The coroutine body calls the suspend delay method and waits for the result with the await suspend function, The await returns COROUTINE_SUSPENDED when the async coroutine is not finished, so the invokeSuspend method of the launch coroutine body directly returns the COROUTINE_SUSPENDED value. The coroutine launched by launch is suspended but does not block the thread, while the coroutine started by async starts execution.
Let’s look at the source of async:
public fun <T> CoroutineScope.async(...).: Deferred<T> {
val newContext = newCoroutineContext(context)
val coroutine = if (start.isLazy) LazyDeferredCoroutine(newContext, block) else
DeferredCoroutine<T>(newContext, active = true)
coroutine.start(start, coroutine, block)
return coroutine
}
Copy the code
By default, the above coroutine takes an instantiation of DeferredCoroutine, so let’s take a look at the await method and how to resume the launch coroutine after the async coroutine has completed its execution:
private open class DeferredCoroutine<T>(
parentContext: CoroutineContext, active: Boolean
) : AbstractCoroutine<T>(parentContext, active), Deferred<T>, SelectClause1<T> {
override suspend fun await(a): T = awaitInternal() as T
}
// JobSupport
internal suspend fun awaitInternal(a): Any? {
while (true) { // lock-free loop on state
val state = this.state
if (state !is Incomplete) {
// Return the result
if (state is CompletedExceptionally) { // Slow path to recover stacktrace
recoverAndThrow(state.cause)
}
return state.unboxState()
}
// If no retry is required, perform awaitSuspend
if (startInternal(state) >= 0) break
}
return awaitSuspend() // slow-path
}
/ / suspendCoroutineUninterceptedOrReturn: access to current coroutines, and suspend the current coroutines (returning COROUTINE_SUSPENDED) or hang directly to return the result
private suspend fun awaitSuspend(a): Any? = suspendCoroutineUninterceptedOrReturn { uCont ->
val cont = AwaitContinuation(uCont.intercepted(), this)
cont.disposeOnCancellation(invokeOnCompletion(ResumeAwaitOnCompletion(this, cont).asHandler))
cont.getResult()
}
Copy the code
The general logic of awaitInternal above is to return the suspended function directly if it already has a result, otherwise suspend the parent coroutine, and then the invokeOnCompletion method inserts ResumeAwaitOnCompletion into a queue (state.list), The source code is no longer posted. Let’s see how ResumeAwaitOnCompletion is called after async has completed to resume the suspended coroutine. Note: Don’t get into the logic of how delay suspends and restores async coroutines in the body of the async coroutine. We don’t need to worry about this layer!
Then async coroutines downwards see, from the front it invokes BaseContinuationImpl. ResumeWith method to execute coroutines logic, we take a look at this method in detail, will perform the function of coroutines invokeSuspend here:
internal abstract class BaseContinuationImpl(
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) {
with(current) {
val completion = completion!! // fail fast when trying to resume continuation without completion
valoutcome: Result<Any? > =try {// Invoke the invokeSuspend method to execute the coroutine logic
val outcome = invokeSuspend(param)
// When coroutine is suspended, return COROUTINE_SUSPENDED. When coroutine is suspended, resumeWith is finished
// When resumeWith is called again, the coroutine is suspended from the starting point of the code to continue execution
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
}
}
}
}
}
Copy the code
We can see from the above source code, in createCoroutineUnintercepted approach is to create instances of SuspendLambda BaseContinuationImpl subclass object, its completion parameters as under:
- launch:
if (isLazy) LazyStandaloneCoroutine else StandaloneCoroutine
- async:
if (isLazy) LazyDeferredCoroutine else DeferredCoroutine
The above classes are subclasses of AbstractCoroutine. Depending on the type of completion, different logic is performed:
- BaseContinuationImpl: Executes coroutine logic
- Others: Call the resumeWith method, which handles the state of the coroutine, and is concerned with resuming the coroutine after it has been suspended
In the above example, the coroutine started by async will also call its invokeSuspend method to perform the async coroutine logic, assuming that the result returned by async is already available, that is, a non-Coroutine_suspended value. The completion is DeferredCoroutine object, therefore calls DeferredCoroutine. ResumeWith method, and then return, father coroutines recovery logic is here.
// AbstractCoroutine
public final override fun resumeWith(result: Result<T>) {
val state = makeCompletingOnce(result.toState())
if (state === COMPLETING_WAITING_CHILDREN) return
afterResume(state)
}
Copy the code
In makeCompletingOnce approach, depending on the state of the state to deal with coroutines, and perform the above state. The list in the queue ResumeAwaitOnCompletion. Invoke to restore the father coroutines, If necessary, I will also give it the result of async, but the specific code implementation is too much to paste, not the focus of this section. Directly see ResumeAwaitOnCompletion. Invoke method:
private class ResumeAwaitOnCompletion<T>(
job: JobSupport, private val continuation: CancellableContinuationImpl<T>
) : JobNode<JobSupport>(job) {
override fun invoke(cause: Throwable?). {
val state = job.state
assert { state !is Incomplete }
if (state is CompletedExceptionally) {
// Resume with with the corresponding exception to preserve it
continuation.resumeWithException(state.cause)
} else {
// Resume the suspended coroutine
continuation.resume(state.unboxState() as T)
}
}
}
Copy the code
Here is a continuation of launch coroutines, namely SuspendLambda object, then invoke method will call again to BaseContinuationImpl. ResumeWith method, Then call SuspendLambda invokeSuspend, then according to the label values continue to perform the following logic!
suspendCoroutineUninterceptedOrReturn
Let’s have a look at how to set a callback based method is converted into a suspend method based on coroutines, in order to achieve this requirement, the emphasis is on suspendCoroutineUninterceptedOrReturn method, according to the comments, the role of this method is: Obtains the current continuation instance inside suspend functions and either suspends currently running coroutine or returns result immediately without suspension. That is, get an instance of the current coroutine and suspend the current coroutine or return the result without suspending it. The function is defined as follows:
public suspend inline fun <T> suspendCoroutineUninterceptedOrReturn(crossinline block: (Continuation<T- > >)Any?).: T {
// ...
}
Copy the code
Depending on the return value of the block, there are two cases:
- If a block returns COROUTINE_SUSPENDED, it means that the suspend function suspends the current coroutine without immediately returning a result. In this case, a Continuation in a block needs to call Continuation.resumeWith to resume the coroutine after the result is available.
- If the T returned by the block is the result of the suspend function, the coroutine is not suspended and the Continuation in the block is not called.
Calling continuation.resumeWith resumes the coroutine directly on the caller’s thread, without passing through the ContinuationInterceptor that may exist in the CoroutineContext. It is recommended to use more secure suspendCoroutine method, in the block can be synchronous or asynchronous thread calls Continuation. Resume and Continuation. ResumeWithException:
public suspend inline fun <T> suspendCoroutine(crossinline block: (Continuation<T- > >)Unit): T {
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
return suspendCoroutineUninterceptedOrReturn { c: Continuation<T> ->
// Call the interceptor
val safe = SafeContinuation(c.intercepted())
block(safe)
safe.getOrThrow()
}
}
Copy the code
In addition to the suspendCoroutine method, There is also suspendCancellableCoroutine, suspendAtomicCancellableCoroutine, SuspendAtomicCancellableCoroutineReusable method can be used to put an asynchronous callback methods encapsulated into suspend function.
Here’s an example of how to wrap an asynchronous callback as a suspend function:
class NetFetcher {
Wrap the request method below into the Suspend method
suspend fun requestSuspend(id: Int): String = suspendCoroutine { continuation ->
request(id, object : OnResponseListener {
override fun onResponse(response: String) {
continuation.resume(response)
}
override fun onError(error: String) {
continuation.resumeWithException(Exception(error))
}
})
}
fun request(id: Int, listener: OnResponseListener) {
Thread.sleep(5000)
if (id % 2= =0) {
listener.onResponse("success")}else {
listener.onError("error")}}interface OnResponseListener {
fun onResponse(response: String)
fun onError(error: String)}}object Main {
fun main(a) {
requestByCoroutine()
}
// Use callbacks
private fun requestByCallback(a) {
NetFetcher().request(21.object : NetFetcher.OnResponseListener {
override fun onResponse(response: String) {
println("result = $response")}override fun onError(error: String) {
println("result = $error")}}}// Use coroutines
private fun requestByCoroutine(a) {
GlobalScope.launch(Dispatchers.Main) {
val result = withContext(Dispatchers.IO) {
try {
NetFetcher().requestSuspend(22)}catch (e: Exception) {
e.message
}
}
println("result = $result")}}}Copy the code
To deepen the understanding, and then introduce Kotlin provides two suspendCancellableCoroutine gain hangs function: delay & yield.
delay
Delay method with the aid of the suspendCancellableCoroutine to suspend coroutines:
public suspend fun delay(timeMillis: Long) {
if (timeMillis <= 0) return // don't delay
return suspendCancellableCoroutine sc@ { cont: CancellableContinuation<Unit> ->
cont.context.delay.scheduleResumeAfterDelay(timeMillis, cont)
}
}
override fun scheduleResumeAfterDelay(timeMillis: Long, continuation: CancellableContinuation<Unit>) {
postDelayed(Runnable {
with(continuation) { resumeUndispatched(Unit) }
}, timeMillis)
}
Copy the code
It can be seen that the logic of delay is similar to the Handle mechanism. It puts the Runnable encapsulated in resumeUndispatched into a queue and executes the resume coroutine when the delay time reaches.
yield
Yield suspends the current coroutine so that other logic can run on the thread in which the coroutine is running. When other coroutines finish executing or yield is also called, the previous coroutine can resume execution.
launch(Dispatchers.Main) {
repeat(3) {
println("job1 $it")
yield()
}
}
launch(Dispatchers.Main) {
repeat(3) {
println("job2 $it")
yield()
}
}
// output
job1 0
job2 0
job1 1
job2 1
job1 2
job2 2
Copy the code
Take a look at the yield source:
public suspend fun yield(a): Unit = suspendCoroutineUninterceptedOrReturn sc@ { uCont ->
val context = uCont.context
// If the coroutine has no scheduler or, like Unconfined, has no scheduler, return directly
val cont = uCont.intercepted() as? DispatchedContinuation<Unit> ?: return@sc Unit
if (cont.dispatcher.isDispatchNeeded(context)) {
// this is a regular dispatcher -- do simple dispatchYield
cont.dispatchYield(context, Unit)}else {
// This is either an "immediate" dispatcher or the Unconfined dispatcher
// ...
}
COROUTINE_SUSPENDED
}
// DispatchedContinuation
internal fun dispatchYield(context: CoroutineContext, value: T) {
_state = value
resumeMode = MODE_CANCELLABLE
dispatcher.dispatchYield(context, this)}public open fun dispatchYield(context: CoroutineContext, block: Runnable): Unit = dispatch(context, block)
Copy the code
DispatchYield calls the dispatcher.dispatch method to distribute the coroutine to the scheduler queue, so that the thread can execute other coroutines and resume the coroutine when the scheduler executes it again.
conclusion
Through the above coroutine working principle analysis, can be found from the source code Kotlin coroutine there are three layers of packaging:
- The first layer of packaging: the Job returned by launch & async, Deferred inherits from AbstractCoroutine, encapsulates the state of the coroutine and provides interfaces such as Cancel.
- The second layer of wrapping: the SuspendLambda subclass generated by the compiler, which encapsulates the true execution logic of the coroutine
SuspendLambda -> ContinuationImpl -> BaseContinuationImpl
, its Completion parameter is the first wrapper instance; - The third wrapper, DispatchedContinuation, encapsulates the thread scheduling logic, and its continuation argument is the second wrapper instance.
Each of these three layers of wrapping implements the Continuation interface, using the proxy pattern to combine the layers of wrapping of coroutines, each of which is responsible for a different function, as shown below:
Reference: www.jianshu.com/p/2979732fb…