基础知识笔记:协程基础元素

Posted 且听真言

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基础知识笔记:协程基础元素相关的知识,希望对你有一定的参考价值。

        一、协程基础元素

        Kotlin 协程的基础元素:Continuation、SafeContinuation、CoroutineContext、CombinedContext、CancellationException、intrinsics。CombinedContext是 CoroutineContext 的一个实现类, SafeContinuation是 Continuation 的实现类。

        1.Continuation 是什么?

class Call<T>(callBack: Call.CallBack<T>) 

    fun cancel() 

    

    interface CallBack<T> 
        fun onSuccess(data: T)
        fun onFail(throwable: Throwable)
    


suspend fun <T : Any> Call<T>.await(): T =
    suspendCancellableCoroutine<T>  continuation ->
        val call = Call(object : Call.CallBack<T> 
            override fun onSuccess(data: T) 
                continuation.resume(data, onCancellation = null)
            

            override fun onFail(throwable: Throwable) 
                continuation.resumeWithException(throwable)
            
        )

        continuation.invokeOnCancellation 
            call.cancel()
        
    

        要实现挂起函数的时候,可以使用 suspendCoroutine、suspendCancellableCoroutine这两个高阶函数,在它们的 Lambda 当中,我们可以使用它暴露出来的 continuation 对象,把程序的执行结果或异常传到外部去。常用于实现挂起函数内部逻辑。

        

fun checkLength() 
    runBlocking 
        val result = getStrLengthSuspend("Kotlin")
        println(result)
    


suspend fun getStrLengthSuspend(text:String):Int = suspendCoroutine 
    continuation ->
    thread 
        Thread.sleep(1000L)
        continuation.resume(text.length)
    


Log
6

Process finished with exit code 0

         使用 suspendCoroutine实现了挂起函数,在它内部,使用 continuation.resume() 的方式,传出了挂起函数的返回值。

        为什么以 continuation.resume() 这样异步的方式传出结果,挂起函数就能接收到结果呢?

suspend fun getStrLengthSuspend(text: String): Int = suspendCoroutine  continuation ->
    thread 
        Thread.sleep(1000L)
        continuation.resume(text.length)
    


fun checkLength2() 
    val func = ::getStrLengthSuspend as (String, Continuation<Int>) -> Any?
    func("Kotlin", object :Continuation<Int> 
        override val context: CoroutineContext
            get() = EmptyCoroutineContext

        override fun resumeWith(result: Result<Int>) 
            println(result.getOrNull())
        
    )

    Thread.sleep(2000L)



Log
6

Process finished with exit code 0


 把函数强转成了带有 Continuation 的函数类型,然后通过匿名内部类的方式,创建了一个 Continuation 对象传了进去。挂起函数的本质,就是 Callback!

把 Continuation 改为 Callback:

fun getStrLength() 
    func2("Kotlin", object : MyCallBack<Int> 
        override fun resume(value: Int) 
            println(value)
        
    )


fun func2(text: String, callBack: MyCallBack<Int>) 
    thread 
        Thread.sleep(1000L)
        callBack.resume(text.length)
    


interface MyCallBack<T> 
    fun resume(value: T)


Log

6

Process finished with exit code 0

 Continuation 改成 Callback 以后,使用匿名内部类创建 Callback 用于接收异步结果;异步函数内部,使用 callback.resume() 将结果传出去。

Kotlin 协程当中的 Continuation,作用其实就相当于 Callback,它既可以用于实现挂起函数,往挂起函数的外部传递结果;也可以用于调用挂起函数,我们可以创建 Continuation 的匿名内部类,来接收挂起函数传递出来的结果。

Java 代码中如何调用 Kotlin 的挂起函数吗?

  public static void main(String[] args) 
        SuspendFromJavaDo.INSTANCE.getUserInfo(new Continuation<String>() 
            @NonNull
            @Override
            public CoroutineContext getContext() 
                return EmptyCoroutineContext.INSTANCE;
            

            @Override
            public void resumeWith(@NonNull Object o) 
                System.out.println(o + "");
            
        );
    



object SuspendFromJavaDo 
    suspend fun getUserInfo():String 
        delay(1000L)
        return "Kotlin"
    

所以,实现挂起函数逻辑的时候,常用到 suspendCoroutine、suspendCancellableCoroutine。

Continuation.kt源码

public interface Continuation<in T> 
    /**
     * The context of the coroutine that corresponds to this continuation.
     */
    public val context: CoroutineContext

    /**
     * Resumes the execution of the corresponding coroutine passing a successful or failed [result] as the
     * return value of the last suspension point.
     */
    public fun resumeWith(result: Result<T>)

public suspend inline fun <T> suspendCoroutine(crossinline block: (Continuation<T>) -> Unit): T 
    contract  callsInPlace(block, InvocationKind.EXACTLY_ONCE) 
    return suspendCoroutineUninterceptedOrReturn  c: Continuation<T> ->
        val safe = SafeContinuation(c.intercepted())
        block(safe)
        safe.getOrThrow()
    

suspendCoroutineUninterceptedOrReturn实现了suspendCoroutine。

val safe = SafeContinuation(c.intercepted()) 包裹Continuation。

block(safe)调用 Lambda 当中的逻辑。

safe.getOrThrow() 取出 block(safe) 的运行结果,Continuation 当中是可以存储 result 的。这个 Result 可能是正确的结果,也可能是异常。

@Suppress("UNUSED_PARAMETER", "RedundantSuspendModifier")
public suspend inline fun <T> suspendCoroutineUninterceptedOrReturn(crossinline block: (Continuation<T>) -> Any?): T 
    contract  callsInPlace(block, InvocationKind.EXACTLY_ONCE) 
    throw NotImplementedError("Implementation of suspendCoroutineUninterceptedOrReturn is intrinsic")

为什么这个高阶函数的源代码会是抛出异常呢?

“Implementation of suspendCoroutineUninterceptedOrReturn is intrinsic.”suspendCoroutineUninterceptedOrReturn 是一个编译器内建函数,它是由 Kotlin 编译器来实现的。

suspendCoroutineUninterceptedOrReturn 这个高阶函数的参数,它会接收一个 Lambda,类型是(Continuation<T>) -> Any?,这里的“Any?”类型,其实就能代表当前这个挂起函数是否真正挂起。

fun test() 
    runBlocking 
        val result = testNoSuspendCoroutinue()
        println(result)
    



private suspend fun testNoSuspendCoroutinue() = suspendCoroutineUninterceptedOrReturn<String>
    continuation ->
    return@suspendCoroutineUninterceptedOrReturn "Kotlin"


Log
Kotlin

Process finished with exit code 0

直接使用 suspendCoroutineUninterceptedOrReturn 实现了挂起函数,并且,在它的 Lambda 当中,我们并没有调用 continuation.resume()。在挂起函数的外部确实也可以接收到这个结果。

 private static final Object testNoSuspendCoroutinue(Continuation $completion) 
      int var2 = false;
      if ("Kotlin" == IntrinsicsKt.getCOROUTINE_SUSPENDED()) 
         DebugProbesKt.probeCoroutineSuspended($completion);
      

      return "Kotlin";
   

这个函数其实就是一个伪挂起函数,它的内部并不会真正挂起。这样,当我们从外部调用这个函数的时候,这个函数会立即返回结果。

private suspend fun testSuspendCoroutinues() =
    suspendCoroutineUninterceptedOrReturn<String>  continuation ->
        thread 
            Thread.sleep(2000L)
            continuation.resume("Kotlin")
        
        return@suspendCoroutineUninterceptedOrReturn kotlin.coroutines.intrinsics.COROUTINE_SUSPENDED
    

fun main() 
    runBlocking 
        val testSuspendCoroutinues = testSuspendCoroutinues()
        println(testSuspendCoroutinues)

    



Kotlin

Process finished with exit code 0

使用了 continuation.resume(),挂起函数的外部也能接收到这个结果。

   private static final Object testSuspendCoroutinues(Continuation $completion) 
      int var2 = false;
      ThreadsKt.thread$default(false, false, (ClassLoader)null, (String)null, 0, (Function0)(new TestCoroutine777Kt$testSuspendCoroutinues$2$1($completion)), 31, (Object)null);
      Object var10000 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
        //将 var10000 赋值为 COROUTINE_SUSPENDED 这个挂起标志位。
      if (var10000 == IntrinsicsKt.getCOROUTINE_SUSPENDED()) 
         DebugProbesKt.probeCoroutineSuspended($completion);
      
//返回挂起标志位,代表 testSuspendCoroutinues() 这个函数会真正挂起。

      return var10000;
   


BuildersKt.runBlocking$default((CoroutineContext)null, (Function2)(new Function2((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 = TestCoroutine777Kt.testSuspendCoroutinues(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");
            

            String testSuspendCoroutinues = (String)var10000;
            System.out.println(testSuspendCoroutinues);
            return Unit.INSTANCE;
         

         @NotNull
         public final Continuation create(@Nullable Object value, @NotNull Continuation completion) 
            Intrinsics.checkNotNullParameter(completion, "completion");
            Function2 var3 = new <anonymous constructor>(completion);
            return var3;
         

         public final Object invoke(Object var1, Object var2) 
            return ((<undefinedtype>)this.create(var1, (Continuation)var2)).invokeSuspend(Unit.INSTANCE);
         
      ), 1, (Object)null);

由于 suspend 修饰的函数,既可能返回 CoroutineSingletons.COROUTINE_SUSPENDED,也可能返回实际结果,甚至可能返回 null,为了适配所有的可能性,CPS 转换后的函数返回值类型就只能是 Any? 了。

suspendCoroutineUninterceptedOrReturn这个高阶函数的作用了:它可以将挂起函数当中的 Continuation 以参数的形式暴露出来,在它的 Lambda 当中,我们可以直接返回结果,这时候它就是一个“伪挂起函数”;或者,我们也可以返回 COROUTINE_SUSPENDED 这个挂起标志位,然后使用 continuation.resume() 传递结果。相应的,suspendCoroutine、suspendCancellableCoroutine这两个高阶函数,只是对它的一种封装而已。

以上是关于基础知识笔记:协程基础元素的主要内容,如果未能解决你的问题,请参考以下文章

Android 基础面内容——Rxjava与协程(含参考答案)

Python协程学习笔记

字节内部超全Kotlin学习笔记,快速上手 Kotlin 开发,基础 + 实战 + 源码,手把手带你吃透 Kotlin 语法与协程。

launch原理解析

Android学习笔记 布局基础

UnityUnity学习笔记目录整理