launch原理解析_createcoroutinefromsuspendfunction,flutter开发视频播放器

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新HarmonyOS鸿蒙全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img

img
img
htt

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上鸿蒙开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip204888 (备注鸿蒙)
img

正文

): Continuation =
SafeContinuation(createCoroutineUnintercepted(receiver, completion).intercepted(), COROUTINE_SUSPENDED)

//启动协程
public fun (suspend () -> T).startCoroutine(
completion: Continuation
) {
createCoroutineUnintercepted(completion).intercepted().resume(Unit)
}

这里可以发现createCoroutine{}和startCoroutine{}都是扩展函数,而且扩展的接收者类型是(suspend () -> T),或许对Kotlin不熟悉的开发者对这个"给函数类型增加扩展"这种写法有点不适应,但是Kotlin中函数就是一等公民,普通类型可以有扩展,那函数类型自然也有。

那上面这个函数该如何使用呢,我们看一下下面代码:

fun main() {
testStartCoroutine()
Thread.sleep(2000L)
}

val block = suspend {
println(“Hello!”)
delay(1000L)
println(“World!”)
“Result”
}

private fun testStartCoroutine() {

val continuation = object : Continuation {
override val context: CoroutineContext
get() = EmptyCoroutineContext

override fun resumeWith(result: Result) {
println(“Result is: ${result.getOrNull()}”)
}
}

block.startCoroutine(continuation)
}

  • 这里定义了变量名为block的lambda表达式,它的类型是 suspend () -> String,其中lambda表达式最后一行表示返回类型。
  • 由于在block中调用了delay(1000)这个挂起函数,所以block也必须是挂起函数类型,这里也就是必须要添加suspend关键字的原因。
  • 定义了一个continuation变量,根据前一篇文章我们知道Continuation有2个作用:一种是在实现挂起函数的时候,用于传递挂起函数的执行结果;另一种是在调用挂起函数的时候,以匿名内部类的方式,接收挂起函数的执行结果。而上面代码的作用就是第二种,用来接收挂起函数或者叫做挂起block的lambda或者叫做协程的执行结果。

上面3点是代码的解读,根据之前实现挂起函数原理中的介绍,这里应该有一个Continuation实例对象,然后调用这个对象的resume方法,把结果返回,我们来看一下另一个底层API:createCoroutine{}或许就有了新的理解:

private fun testStartCoroutine() {

val continuation = object : Continuation {
override val context: CoroutineContext
get() = EmptyCoroutineContext

override fun resumeWith(result: Result) {
println(“Result is: ${result.getOrNull()}”)
}
}

val coroutine = block.createCoroutine(continuation)
coroutine.resume(Unit)
}

可以发现我们这里返回值变量名就叫做coroutine,顾名思义就是一个协程,而这个协程的类型还是Continuation,但是这里并没有启动,只有调用了resume()方法才真正的启动协程。

所以底层基础创建协程API:createCoroutine{}和startCoroutine{}的差别也就是前者没有调用resume(),后者调用了resume()而已。

这里我们又可以猜测,根据挂起函数CPS和状态机原理**,调用continuation的resume方法会触发invokeSuspend()方法进入状态机**,我们下面就来看看startCoroutine()的原理。

startCoroutine{}原理解析

这里我们直接把上面代码进行反编译,可以得到如下代码:

public final class TestCoroutine1Kt {

//注释1,main()函数
public static final void main() {
testStartCoroutine();
Thread.sleep(2000L);
}

public static void main(String[] var0) {
main();
}

//注释2,block变量
@NotNull
private static final Function1 block;

@NotNull
public static final Function1 getBlock() {
return block;
}

//注释3,testStartCoroutine函数
private static final void testStartCoroutine() {
//匿名内部类实例,用于接收挂起函数的结果
continuation = new Continuation() {
@NotNull
public CoroutineContext getContext() {
return (CoroutineContext)EmptyCoroutineContext.INSTANCE;
}

public void resumeWith(@NotNull Object result) {
String var2 = "Result is: " + (String)(Result.isFailure-impl(result) ? null : result);
System.out.println(var2);
}
};
//调用startCoroutine高阶函数
ContinuationKt.startCoroutine(block, (Continuation)continuation);
}

//注释4,block对应的匿名内部类实例
static {
Function1 var0 = (Function1)(new Function1((Continuation)null) {
int label;

//注释5,进入状态机
public final Object invokeSuspend(@NotNull Object KaTeX parse error: Expected '}', got 'EOF' at end of input: …throwOnFailure(result);
var2 = “Hello!”;
System.out.println(var2);
this.label = 1;
if (DelayKt.delay(1000L, this) == var3) {
return var3;
}
break;
case 1:
ResultKt.throwOnFailure($result);
break;
default:
throw new IllegalStateException(“call to ‘resume’ before ‘invoke’ with coroutine”);
}

var2 = “World!”;
System.out.println(var2);
return “Result”;
}

//注释6,创建匿名内部类实例
public final Continuation create(@NotNull Continuation completion) {
Intrinsics.checkNotNullParameter(completion, “completion”);
Function1 var2 = new (completion);
return var2;
}

//Function1接口的函数调用
public final Object invoke(Object var1) {
return (()this.create((Continuation)var1)).invokeSuspend(Unit.INSTANCE);
}
});
block = var0;
}
}

上面反编译代码较多,我们来按照注释,仔细分析一下:

  1. 注释1是我们的main()函数,反编译后逻辑不会变化。
  2. 注释2是block变量,注意这里block在Kotlin代码中的类型是 suspend () -> String,然后根据CPS转换(这里也可以看成是一个挂起函数,毕竟和挂起函数类型是一样的,都是高阶函数类型)类型是(Continuation< String>) -> Any?,所以这里block的类型是Function1。
  3. 注释3就是testCoroutine()方法了,根据Kotlin的顶层函数语法,原来的block.startCoroutine(continuation)就变成了ContinuationKt.startCoroutine(block,continuaiton),这个函数调用原理后面细说。
  4. 注释4就是block具体实现类实例,首先我们可以看一下其中的方法:首先是注释5的invokeSuspend()函数,这个在之前说挂起函数原理时说过,这个是ContinuationImpl中定义的方法,而ContinuationImpl是Continuation的子类,所以其实现类既是Function1接口的实现类,也是Continuation子类。而这里的注释5,就是进入状态机,开始协程业务逻辑执行。
  5. 其次就是注释6的create方法,这里传入一个Continuation类型的completion实例,而且会调用返回整个匿名内部类实例,即这里会返回var0,即block实例。

上面第5点暂时不细说,我们先来看看testStartCoroutine()函数中的startCoroutine()方法:

public fun (suspend () -> T).startCoroutine(
completion: Continuation
) {
createCoroutineUnintercepted(completion).intercepted().resume(Unit)
}

这里调用了createCoroutineUnintercepted()方法:

public expect fun (suspend () -> T).createCoroutineUnintercepted(
completion: Continuation
): Continuation

会发现这里是用expect修饰的,即是一种声明,我们需要到协程源代码的JVM实现部分中找到对应的实现:

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

可以发现这也是(suspend () -> T)的扩展函数,所以this其实就是前面代码中的block,而前面说了反编译中block的实现类类型是继承至ContinuationImpl的,所以注释2的第一个if就能返回ture,而这里就是调用create(probeCompletion)函数。

而这个create()方法就是前面反编译中block实现类的create()方法:

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

注意了,这里返回值是Continuation类型对象,即调用完create()方法,其实就对应着协程被创建了,和挂起函数一样,类型是Continuation类型。

所以这里就好办了,根据前面的知识,这时调用resume,便会触发协程体的状态机入口,所以:

public fun (suspend () -> T).startCoroutine(
completion: Continuation
) {
createCoroutineUnintercepted(completion).intercepted().resume(Unit)
}

这里的最后调用就是resume(Unit),调用完resume就会调用continuation的invokeSuspend方法,从而开启协程的执行。

注意上面在resume()方法调用之前,还调用了intercepted()方法,我们简单看一下:

public expect fun Continuation.intercepted(): Continuation

这个方法在Continuation.kt类中,是基础元素,同时也是用expect修饰的,所以我们要去Kotlin源码中找到JVM平台的实现:

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

这里逻辑非常简单,就是将Continuation强转为ContinuationImpl,然后调用它的intercpeted()方法,而前面我们说过block实现类就是这个类的子类,所以强转一定能成功,而这个方法如下:

internal abstract class ContinuationImpl(
completion: Continuation<Any?>?,
private val _context: CoroutineContext?
) : BaseContinuationImpl(completion) {

@Transient
private var intercepted: Continuation<Any?>? = null

public fun intercepted(): Continuation<Any?> =
intercepted
?: (context[ContinuationInterceptor]?.interceptContinuation(this) ?: this)
.also { intercepted = it }
}

这里的逻辑其实就是通过ContinuationInterceptor类来对Continuation进行拦截和处理,而这里的处理其实就是将协程派发到线程上,这部分知识点等我们说Dispatchers时再细说。

所以到这里我们就大致说明白了底层启动协程API的原理,其中block就是一个协程,它的类型必须是suspend类型的,然后本质就是一个内部类,父类是Function1和Continuation,创建完协程就是返回一个内部类实例,而这个实例类型就是Continuation。

然后调用resume方法来触发Continuation进入其状态机

launch启动协程

其实有了startCoroutine{}这种基础API,中间层的launch、async、runBlocking这些API就是对基础底层API的封装,所以在理解完前面的原理后,这几个API就很好分析了。

我们还是先来看个例子:

fun main() {
testLaunch()
Thread.sleep(2000L)
}

private fun testLaunch() {
val scope = CoroutineScope(Job())
scope.launch {
println(“Hello!”)
delay(1000L)
println(“World!”)
}
}

这里我们创建了一个scope,然后使用这个scope开启了一个协程,我们直接反编译上面代码,看一下Java代码:

public final class TestCoroutine1Kt {

//注释1 main()函数
public static final void main() {
testLaunch();
Thread.sleep(2000L);
}

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注鸿蒙)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

public final class TestCoroutine1Kt {

//注释1 main()函数
public static final void main() {
testLaunch();
Thread.sleep(2000L);
}

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注鸿蒙)
[外链图片转存中…(img-bxj10Eo1-1713306947410)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 25
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值