In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-16 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/01 Report--
This article mainly introduces what is the principle of Coroutine cooperation in Android. It has a certain reference value. Interested friends can refer to it. I hope you will gain a lot after reading this article. Let's take a look at it.
Preface
Co-programming is a concurrent scheme. It's also a kind of thought.
In the traditional sense, the cooperative process is single-threaded, and it consumes less memory and is more efficient in the face of io-intensive tasks. However, in the face of computing-intensive tasks, it is not as efficient as multi-thread parallel computing.
Different languages have different implementations for collaborative programs, and even the same language has corresponding implementations for operating systems on different platforms.
The cooperation process of our kotlin language is the implementation of coroutines for jvm. The underlying principle is also the use of java threads.
Ecological framework of basic knowledge
Related dependency library dependencies {/ / Kotlin implementation "org.jetbrains.kotlin:kotlin-stdlib:1.4.32" / / implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.3" / / Cooperative Android support library implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.3" / / Cooperative Java8 support library implementation "org.jetbrains.kotlinx:kotlinx-coroutines-jdk8: 1.4.3 "/ / lifecycle implementation" androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0 "implementation" androidx.lifecycle:lifecycle-runtime-ktx:2.2.0 "implementation" androidx.lifecycle:lifecycle-livedata-ktx:2.2.0 "why do some people always find collaborative programming obscure?
1. There is no detailed definition of the concept of collaborative process on the network, and the implementation of each language and system is different. It can be said that there are different opinions, what kernel mode user mode Barabara, it is easy to bias us.
The interference caused by various grammatical sugars of 2.kotlin to us. Such as:
Higher order function
The source code implementation class could not be found
Therefore, a solid basic skill of kotlin grammar is a prerequisite for learning the cooperative process.
Really do not understand the place to decompile to java, to java final translation shall prevail.
What is a cooperative journey? What's the use?
What the co-program in kotlin does is flatten the asynchronous callback code, straighten it out, and synchronize the asynchronous callback code. Apart from that, there is nothing special about it.
To create a co-program is to secretly generate a series of code behind the compiler, such as a state machine.
Make the state machine state flow by suspending and resuming to make the layer upon layer of nested callback code as intuitive and concise as synchronous code.
It is not a threaded framework, nor is it a sophisticated kernel state, user state. In fact, for us Android, it is a grammatical sugar about callback functions.
This article will thoroughly analyze the implementation principle of the collaborative process around suspension and recovery.
Review of basic knowledge of Kotlin function
In Kotlin, functions are first-class citizens and have their own types.
Function type
It can also be written as (Foo,String,Long)-> Any
Function reference
Fun foo () {} / / reference is:: foofun foo (p0: Int): String// reference is also:: foo
Why are they all the same? I can't help it. It's just a rule. When using it, it can only be inferred by the compiler.
Val f: ()-> Unit =:: foo / / the compiler will infer that it is fun foo () {} val g: (Int)-> String =:: foo / / infer as fun foo (p0: Int): String
Writing with Receiver
Class Foo {fun bar (p0: String,p1: Long): Any {}} val h: (Foo,String,Long)-> Any = Foo:bar
The function reference that binds receiver:
Val foo: Foo = Foo () val m: (String,Long)-> Any = foo:bar
Additional knowledge points
Val x: (Foo,String,Long)-> Any = Foo:barval y: Function3 = xFoo. (String,Long)-> Any = (Foo,String,Long)-> Any = Function3
Function is passed as an argument
Fun yy (p: (Foo,String,Long)-> Any) {p (Foo (), "Hello", 3L) / / directly p () can call / / p.invoke (Foo (), "Hello", 3L) can also be in invoke form}
Lambda
It's an anonymous function, which has no name compared to an ordinary function, and it sounds like nonsense.
/ / ordinary function fun func () {println ("hello");} / remove the function name func, it becomes anonymous function fun () {println ("hello"); / / the type that can be assigned to a variable val func= fun () {/ / anonymous function val func: ()-> Unit = fun () {/ / Lambda expression val func= {print ("Hello"); / / Lambda type val func: ()-> String = {print ("Hello") "Hello" / / if it is in Lambda, the last line is treated as a return value, leaving out return. Ordinary functions cannot / / with parameters Lambdaval F1: (Int)-> Unit = {p:Int-> print (p); / / can be further simplified to val F1 = {p:Int-> print (p); / / when there is only one parameter, it can also be written as val F1: (Int)-> Unit = {print (it); personal experience summary of the function
Functions look no different from anonymous functions, but you can still see some differences when decompiled to java
If you only use ordinary functions, then it is no different from ordinary java functions.
For example, fun a () corresponds to the java method public void a () {}
But if you use this function through a function reference (:: a), instead of calling fun a () directly, it will regenerate a Function0.
Suspend function
Suspend modification.
Any function can be called in a suspended function.
Non-pending functions can only call non-pending functions.
In other words, the suspend function can only be called in the suspend function.
A simple suspend function shows:
/ / com.example.studycoroutine.chapter.CoroutineRun.ktsuspend fun suspendFun (): Int {return 1;}
What is so special about the suspend function?
Public static final Object suspendFun (Continuation completion) {return Boxing.boxInt (1);}
Now you can understand why suspend can only be called in suspend?
If you want to make the sanctimonious suspend function work, you must first satisfy it! Is to put a ball in it.
Then he wants to call other suspend functions, just continue stuffing the ball into other suspend methods.
There is no such thing in ordinary functions, so it is impossible to call the suspend function at all.
At this point, you must have some questions:
Question1. Isn't this the problem of raw chicken? Where did the first ball come from?
Question2. Why did the return value change after compilation?
If question3.suspendFun is called in the co-program, who is his completion?
The original tool public fun (suspend ()-> T) .startCoroutine (completion: Continuation) {createCoroutineUnintercepted (completion). Intercepted (). Resume (Unit)} public fun (suspend ()-> T) .createCoroutine (completion: Continuation): Continuation = SafeContinuation (createCoroutineUnintercepted (completion). Intercepted (), COROUTINE_SUSPENDED)
Start a collaborative process in the easiest way.
Demo-K1
()} ") difference between two creation co-programming functions
StartCoroutine does not return a value, while createCoroutine returns a Continuation. It is not difficult to see that it is SafeContinuation.
It seems that the main difference is that startCoroutine calls resume (Unit) directly, so it is not wrapped as SafeContinuation, while createCoroutine returns a SafeContinuation because you don't know when and where resume will be called, so you have to make sure that resume is called only once, so it is wrapped as safeContinuation.
The purpose of SafeContinuationd is to ensure that it hangs only when an asynchronous call occurs
Analyze createCoroutineUnintercepted//kotlin.coroutines.intrinsics.CoroutinesIntrinsicsH.kt@SinceKotlin ("1.3") public expect fun (suspend ()-> T). CreateCoroutineUnqualified (completion: Continuation): Continuation first.
In fact, it can be simply understood as the primitive at the kotlin level, which is to return a protocol body.
Start the analysis
The reference code Demo-K1 is first of all an anonymous function, which must be compiled into a FunctionX, and it is also decorated with suspend, so it must be different from a normal anonymous function after compilation.
The compiled source code is
Public static final void main () {Function1 var0 = (Function1) (new Function1 ((Continuation) null) {int label; @ Nullable public final Object invokeSuspend (@ NotNull Object $result) {Object var3 = IntrinsicsKt.getCOROUTINE_SUSPENDED (); Object var10000; switch (this.label) {case 0: ResultKt.throwOnFailure ($result); this.label = 1 Var10000 = TestSampleKt.hello2 (this); if (var10000 = = var3) {return var3;} break; case 1: var10000 = $result; default: throw new IllegalStateException ("call to 'resume' before' invoke' with coroutine") } int a = ((Number) var10000) .intValue (); return Boxing.boxInt (a);} @ NotNull public final Continuation create (@ NotNull Continuation completion) {Intrinsics.checkParameterIsNotNull (completion, "completion"); Function1 var2 = new (completion); return var2 Public final Object invoke (Object var1) {return (() this.create ((Continuation) var1)) .invokeSuspend (Unit.INSTANCE); boolean var1 = false; Continuation var7 = ContinuationKt.createCoroutine (var0, (Continuation) (newMyContinuation (); Unit var8 = Unit.INSTANCE; boolean var2 = false; Companion var3 = Result.Companion; boolean var5 = false; Object var6 = Result.constructor-impl (var8); var7.resumeWith (var6);}
We can see that first Function1 var0 = new Function1 creates an object, which has nothing to do with the co-program at this time. This step is only anonymous function syntax optimization at the compiler level.
If you directly
Fun main () {suspend {val a = hello2 () a} .createCoroutine (MyContinuation ()) .creative (Unit)}
The same will also create Function1 var0 = new Function1
Answer question1
Continue to call createCoroutine
Continue with createCoroutineUnintercepted and find the implementation on the JVM platform
/ / kotlin.coroutines.intrinsics.IntrinsicsJVM.class@SinceKotlin ("1.3") public actual fun (suspend ()-> T) .createCoroutineUnintercepted (completion: Continuation): Continuation {/ / probeCompletion or we pass in the completion object, which is myCoroutineval probeCompletion = probeCoroutineCreated (completion) / / probeCoroutineCreated method in our Demo. It seems to be used by debug. My understanding is like this / / This is this suspend lambda. In Demo, the myCoroutineFun return if (this is BaseContinuationImpl) create (probeCompletion) else//else branch does not go to / / when [createCoroutineUnintercepted] encounters a pending lambda that does not inherit BaseContinuationImpl. CreateCoroutineFromSuspendFunction (probeCompletion) {(this as Function1) .invoke (it)} @ NotNullpublic final Continuation create (@ NotNull Continuation completion) {Intrinsics.checkNotNullParameter (completion, "completion"); Function1 var2 = new (completion); return var2;}
Pass in completion and create a new Function1, which is returned as Continuation. This is the created protocol body object. The core of the work of the protocol is its internal state machine, invokeSuspend function.
Call create
@ NotNullpublic final Continuation create (@ NotNull Continuation completion) {Intrinsics.checkNotNullParameter (completion, "completion"); Function1 var2 = new (completion); return var2;}
Pass in completion and create a new Function1, which is returned as Continuation. This is the created protocol body object. The core of the work of the protocol is its internal state machine, invokeSuspend function.
Supplement-related class inheritance relationship
Answer question2&3
It is known that the protocol startup will call the resume of the protocol body, which will eventually come to BaseContinuationImpl::resumeWith
Internal abstract class BaseContinuationImpl {fun resumeWith (result: Result) {/ / 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) {with (current) {val completion = completions! / / fail fast when trying to resume continuation without completion val outcome: Result = Try {val outcome = invokeSuspend (param) / / call the state machine 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 {/ / finally got here This completion is the first ball that was stuffed. Completion.resumeWith (outcome) return}
State machine code interception
Public final Object invokeSuspend (@ NotNull Object $result) {Object var3 = IntrinsicsKt.getCOROUTINE_SUSPENDED (); Object var10000; switch (this.label) {case 0result / label = 0 ResultKt.throwOnFailure ($result) for the first time; / / label is changed to 1, which means case 1 will be used the next time it is restored, which is called [state transfer] this.label = 1. / / with all eyes on me, I'd like to announce one thing: the object of this is. Var10000 = TestSampleKt.hello2 (this); if (var10000 = = var3) {return var3;} break; case 1: ResultKt.throwOnFailure ($result); var10000 = $result; break; default: throw new IllegalStateException ("call to 'resume' before' invoke' with coroutine");} int a = ((Number) var10000) .intValue (); return Boxing.boxInt (a);}
The answer to question3 came out, and it was the continuation that create created.
Finally, let's talk about question2. From the above code, it is clear why the function's decompiled return value has been changed to object.
In the case of hello2, hello2 can return either a suspended whiteboard or a result. If the whiteboard is returned, the state machine return is suspended. If result is returned, then hello2 finishes execution and is a pending function without suspension, and the compiler usually reminds suspend that the modifier is meaningless. So that's what the design needs, nothing because of.
Finally, except in the case where the result is returned directly, the pending function must end with resume, either returning result or an exception. Represents that the pending function returns.
The point of calling resume is to call back BaseContinuationImpl's resumeWith, which in turn wakes up the state machine and continues to execute the code of the protocol body.
SuspendCoroutine
We can do this without suspendCoroutine and with a more direct suspendCoroutineUninterceptedOrReturn, but we need to return to the whiteboard manually. But you must be careful to return or not to return under reasonable circumstances, otherwise it will produce a lot of unexpected results.
Suspend fun mySuspendOne () = suspendCoroutineUninterceptedOrReturn {continuation-> thread {TimeUnit.SECONDS.sleep (1) continuation.resume ("hello world")} / / because our function does not return the correct result, we must return a pending flag, otherwise BaseContinuationImpl will think the task is done. / / and our thread is running again without cancellation, which will lead to a lot of unexpected results kotlin.coroutines.intrinsics.COROUTINE_SUSPENDED}
However, suspendCoroutine does not have this hidden danger.
Suspend fun mySafeSuspendOne () = suspendCoroutine {continuation-> thread {TimeUnit.SECONDS.sleep (1) continuation.resume ("hello world")} / / the suspendCoroutine function cleverly helps us determine if the returned result is not the desired object Automatically return kotlin.coroutines.intrinsics.COROUTINE_SUSPENDED} public suspend inline fun suspendCoroutine (crossinline block: (Continuation)-> Unit): t = suspendCoroutineUninterceptedOrReturn {c: Continuation-> / / encapsulate a proxy Continuation object val safe = SafeContinuation (c.intercepted ()) block (safe) / / determine whether to return COROUTINE_SUSPENDED safe.getOrThrow () based on the result returned by block
The Mystery of SafeContinuation
/ / call the constructor internal actual constructor (delegate: Continuation): this (delegate, UNDECIDED) @ Volatileprivate var result: Any? = initialResult / / UNDECIDED the set of things assigned to the result / / java atomic attribute updater private companion object {@ Suppress ("UNCHECKED_CAST") @ JvmStatic privateval RESULT = AtomicReferenceFieldUpdater.newUpdater (SafeContinuation::class.java, Any::class.java as Class) "result")} internal actual fun getOrThrow (): Any? {var result = this.result / / atomic read if (result = = UNDECIDED) {/ / if UNDECIDED Then set result to COROUTINE_SUSPENDED if (RESULT.compareAndSet (this, UNDECIDED, COROUTINE_SUSPENDED)) returnCOROUTINE_SUSPENDED result = this.result / / reread volatile var return when {result = RESUMED-> COROUTINE_SUSPENDED / / already called continuation, indicate COROUTINE_SUSPENDED upstream result is Result.Failure-> throw result.exception else-> result / / either COROUTINE_SUSPENDED or data if (RESULT.compareAndSet (this, UNDECIDED, result.value)) return / / if it is suspended Through the resumeWith callback state machine cur = COROUTINE_SUSPENDED-> if (RESULT.compareAndSet (this, COROUTINE_SUSPENDED, RESUMED)) {delegate.resumeWith (result) return} else-> throw IllegalStateException ("Already resumed")}} val safe = SafeContinuation (c.intercepted ()) block (safe) safe.getOrThrow ()
Let's review what a real hang is, that is, getOrThrow returns a "whiteboard", so when can getOrThrow return a whiteboard? The answer is that the value of result has not been modified after it has been initialized. That is to say, resumeWith has not been executed, that is, the code block (safe), the passed function block, does not call safe's resumeWith during execution. The principle is that simple. Cas code ensures atomicity and concurrency security of key logic.
Continuing to take Demo-K1 as an example, it is assumed that hello2 is running on a new child thread, otherwise it is still not suspended.
{thread {Thread.sleep (1000) it.resume (10086)}} Thank you for reading this article carefully. I hope the article "what is the principle of Coroutine in Android" shared by the editor will be helpful to you. At the same time, I hope you will support us and pay attention to the industry information channel. More related knowledge is waiting for you to learn!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.