Kotlin协程初探

创建协程

首先来创建一个简单的协程,使用挂起修饰符suspend修饰一个lambda函数

val lambda: suspend () -> Int = suspend {
        println("Im coroutine")
        5
    }

对象经编译器编译之后创建的是一个<文件名>Kt$<方法名>$continuation$1的匿名内部类的对象,查看字节码该内部类继承自kotlin.coroutines.jvm.internal.SuspendLambda,实现了kotlin.jvm.functions.Function1
SuspendLambda的继承关系为:SuspendLambda->ContinuationImpl->BaseContinuationImpl->Continuation
标准库中提供了一个createCoroutine函数,可以通过它来创建协程,不过这个协程并不会立即执行,先来看看它的声明:

public fun  (suspend () -> T).createCoroutine(
    completion: Continuation
): Continuation =
    SafeContinuation(createCoroutineUnintercepted(completion).intercepted(), COROUTINE_SUSPENDED)

createCoroutine是对被suspend修饰的lambda的一个扩展函数,方法返回的具体对象是SafeContinuation,现在协程仅仅是被创建出来,需要通过这个返回值在之后触发协程的启动,具体的使用为:

 val continuation = lambda.createCoroutine(object : Continuation {
        override val context: CoroutineContext = EmptyCoroutineContext

        override fun resumeWith(result: Result) {
            println("coroutine End: $result")
        }
    })

参数completion会在协程执行完成后调用,实际上就是协程的完成回调。
SafeContinuation在Kotlin里的声明是平台相关的,具体的实现在平台模块中实现,文件为SafeContinuationJvm。查看SafeContinuation的源码可知其真是身份是个代理类,具体的逻辑在其成员变量delegate中,delegate是在其构造函数中传入的,也就是上面的createCoroutineUnintercepted(completion).intercepted()方法。
createCoroutineUnintercepted方法的声明也是平台相关的,具体的实现在IntrinsicsJvm文件中:

public actual fun  (suspend () -> T).createCoroutineUnintercepted(
    completion: Continuation
): Continuation {
    val probeCompletion = probeCoroutineCreated(completion)
    return if (this is BaseContinuationImpl)
        create(probeCompletion)
    else
        createCoroutineFromSuspendFunction(probeCompletion) {
            (this as Function1, Any?>).invoke(it)
        }
}

probeCoroutineCreated方法返回的就是completion自身,根据上面的继承关系可知lambda继承自BaseContinuationImpl,会调用BaseContinuationImpl的create方法,传入completion,create方法在编译器根据lambda自动生成的文件中进行了覆写,查看lambda的字节码文件声明如下:

@NotNull
public final Continuation create(@NotNull Continuation completion) {
    Intrinsics.checkParameterIsNotNull(completion, "completion");
    Function1 var2 = new (completion);
    return var2;
}

方法里再次调用了lambda生成的匿名内部类的构造函数,并在构造函数中将completion传入,根据继承关系调用父类构造函数,completion最后会传到BaseContinuationImpl的构造函数并被保存下来。
最后看一下intercepted()方法,和createCoroutineUnintercepted一样,具体的实现在IntrinsicsJvm文件中:

public actual fun  Continuation.intercepted(): Continuation =
    (this as? ContinuationImpl)?.intercepted() ?: this

根据继承关系,匿名内部类是ContinuationImpl的子类,调用了ContinuationImplintercepted()方法:

@Transient
private var intercepted: Continuation? = null
public fun intercepted(): Continuation =
    intercepted
        ?: (context[ContinuationInterceptor]?.interceptContinuation(this) ?: this)
            .also { intercepted = it }

在之前的构造方法中没有传入CoroutineContext,此处返回值就是自身。
所以createCoroutineUnintercepted(completion).intercepted()方法返回的就是一个编译器根据lambda生成的匿名内部类的对象,到此一个简单协程的创建流程就梳理完了。

启动协程

调用continuation.resume(Unit)之后,协程体会立即开始执行。resume方法的实现为:

public inline fun  Continuation.resume(value: T): Unit =
    resumeWith(Result.success(value))

此处调用了SafeContinuationresumeWith方法:

public actual override fun resumeWith(result: Result) {
    while (true) { // lock-free loop
        val cur = this.result // atomic read
        when {
            cur === UNDECIDED -> if (RESULT.compareAndSet(this, UNDECIDED, result.value)) return
            cur === COROUTINE_SUSPENDED -> if (RESULT.compareAndSet(this, COROUTINE_SUSPENDED, RESUMED)) {
                delegate.resumeWith(result)
                return
            }
            else -> throw IllegalStateException("Already resumed")
        }
    }
}

result的值为createCoroutine方法中调用SafeContinuation构造函数时传入的COROUTINE_SUSPENDED,所以when方法会走到第二个分支,会调用delegate.resumeWith(result)delegate的对象为BaseContinuationImpl的子类,会调用BaseContinuationImplresumeWith方法:

public final override 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) {
        // Invoke "resume" debug probe on every resumed continuation, so that a debugging library infrastructure
        // can precisely track what part of suspended callstack was already resumed
        probeCoroutineResumed(current)
        with(current) {
            val completion = completion!! // fail fast when trying to resume continuation without completion
            val outcome: Result =
                try {
                    val outcome = invokeSuspend(param)
                    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
            }
        }
    }
}

result为调用resume方法时传入的UnitprobeCoroutineResumed为空实现,invokeSuspendBaseContinuationImpl内的抽象方法,编译器根据lambda生成的匿名内部类中对该方法进行了实现,具体实现查看字节码:

@Nullable
public final Object invokeSuspend(@NotNull Object $result) {
   Object var2 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
   switch(this.label) {
   case 0:
      ResultKt.throwOnFailure($result);
      return Boxing.boxInt(5);
   default:
      throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
   }
}

因为invokeSuspend方法的返回值是Object的,lambda中只返回了一个数字5,此处会对数字进行一下装箱然后进行返回。
resumeWith方法中outcome的值为Integer类型的5,completion为之前createCoroutine传入的Continuation实现类对象,其父类不是BaseContinuationImpl,此时会调用completionresumeWith方法,最后打印出结果为:

coroutine End: Result.Success(5)

至此简单协程的创建与启动就分析完成了。

你可能感兴趣的:(Kotlin协程初探)