kotlin协程-基础设施篇-协程创建与启动:SafeContinuation

种一颗树的最好时机是十年前,其次是现在。 学习也一样。 跟着霍老师的《深入理解Kotlin携程》学习一下协程。

在这里,我们将kotlin中的协程实现分为两个层次

  • 基础设施层:标准的协程API,主要对协程提供了概念和语义上最基本的支持。
  • 业务框架层:协程的上层框架支持

协程的构造

我们可以很快捷的创建一个简单的协程

kotlin 复制代码
    val continuation = suspend {
        println("In Coroutine")
        5
    }.createCoroutine(object : Continuation<Int>{

        override fun resumeWith(result: Result<Int>) {
            println("Coroutine end :$result")
        }
        override val context: CoroutineContext = EmptyCoroutineContext
    })

函数解析

接下来我们仔细的探查一下这个createCoroutine函数。 首先是它的声明:

kotlin 复制代码
public fun <T> (suspend () -> T).createCoroutine(
    completion: Continuation<T>
): Continuation<Unit> =

这里的(suspend () -> T)是函数createCoroutine的Receiver(接收者),简单解释一下:

带接收者的函数类型

是一种特殊的函数类型,它允许您在函数类型中指定一个接收者对象,使得在函数体内可以直接访问该接收者对象的成员函数和属性。这种函数类型的语法是在函数类型声明之前添加接收者类型。 带接收者的函数类型的语法如下:

接收者类型.() -> 返回类型

举个例子:

kotlin 复制代码
data class Person(val name: String)

// 带接收者的函数类型
val greeting: Person.() -> String = {
    "Hello, $name!"
}

// 扩展函数
fun Person.greet(): String {
    return "Hello, $name!"
}
fun main() {
    val person: Person = Person("huang")

    // 使用带接收者的函数类型调用函数
    val message1 = person.greeting()

    // 使用扩展函数调用函数
    val message2 = person.greet()

    println(message1) // 输出: Hello, huang!
    println(message2) // 输出: Hello, huang!
}

详细了解可以看kotlin专栏中的这一篇, Kotlin中的函数类型及Lambda表达式:SAM转换,带接收者的函数类型,匿名函数.

createCoroutine说明

在了解了上面的知识之后,我们再回过头来看下createCoroutine函数

  • Receiver是一个被suspend修饰的挂起函数,这也是协程要执行的代码,就先叫它协程体
  • 它需要一个Continuation<T>类型的参数completion,这个参数会在协程执行完后调用,实际上就是协程的完成回调
  • 返回值是一个 Continuation<Unit>对象,由于createCoroutine只是创建了协程,后面我们还需要使用这个返回对象启动协程

协程启动

启动协程非常简单,只需要调用continuation.resume(Unit)就可以了。

continuation解释

下面我们简单看一下continuation这个对象是啥。打上断点,我们可以看到它是一个SafeContinuation实例。 我们再回过头看下createCoroutine这个函数,实际上返回的就是SafeContinuation实例 这里传入了两个参数,一个是delegate和一个COROUTINE_SUSPENDED。 当我们执行resume的时候,实际上调用的是delegateresumeWith方法。 我们进入SafeContinuation类中,发现该类的resumeWith实际上调用的就是上面传入的delegateresumeWith方法,并且这里的delegate类型是HelloKt$main$continuation$1这个类型特别像是匿名内部类的名字,仔细观察会发现这个匿名类的名字是<FileName>Kt$<FunctionName>$continuation$1这种形式,猜测应该是编译器在编译的过程中添加了一些现在我们还暂时用不到的东西,让我们传入的suspend Lambda表达式生成了匿名内部类

当执行delegate.resume方法时,我们的suspend Lambda 表达式得到执行,并且在执行完毕后,调用了传入的completion参数的resumeWith方法。

其他方式

其实还有一个更方便的创建并启动协程的方法

kotlin 复制代码
    suspend {
        println("In Coroutine")
        5
    }.startCoroutine(object : Continuation<Int>{

        override fun resumeWith(result: Result<Int>) {
            println("Coroutine end :$result")
        }
        override val context: CoroutineContext = EmptyCoroutineContext
    })

实际上只是帮我们调用了一下resume, 哈哈哈,当然了,返回值也不一样了。

协程体的Receiver

除了上面提到的两个方法外,我们还注意到另外两个方法 这两个方法和上面的两个方法,只是多了一个协程体的Receiver类型R。这个R可以为协程体提供一个作用域,在协程体内可以直接使用作用域内提供的函数或者字段。

怎么用

假设我们有一个数据类

kotlin 复制代码
data class Student(val name: String, val age: Int)

创建协程并启动

kotlin 复制代码
 val coroutineBlock : suspend Student.() -> Int = {
     println("In Coroutine ${this.name}")
     this.age
 }

val continuation = coroutineBlock.createCoroutine(Student("John", 23), completion = object : Continuation<Int> {
     override val context: CoroutineContext
         get() = EmptyCoroutineContext
     override fun resumeWith(result: Result<Int>) {
         println("Coroutine end :$result")
     }

 })
continuation.resume(Unit)

这样,我们就可以在suspend Lambda表达式中使用Student类提供函数和字段了。


以上就是协程的创建和启动以及相关的一些零碎知识点,接下来我们一起学习一下函数的挂起

相关推荐
bytebitx12 小时前
MacOS 编译腾讯 Mars XLog 及使用(已16KB对齐)
kotlin·mac·apk
alexhilton1 天前
让架构边界变成可执行的测试
android·kotlin·android jetpack
数据治理自习室2 天前
AI 应用评测体系(GraphRAG)
android·大数据·人工智能·kotlin
名剑走天下3 天前
Android 面试题大全
android·kotlin
chjif3 天前
Android 设备管控开发实战:自定义 Launcher 如何只显示指定 App?
android·kotlin
非凡ghost3 天前
用 Kotlin 和 Jetpack Compose 重写的安卓视频播放器,原生体验有多流畅?
android·java·kotlin·音视频·软件需求
名剑走天下3 天前
android 面试题
kotlin
蜡台4 天前
Jetpack Compose 稳定性、重组优化(Stable / @NonRestartableComposable)
android·kotlin·compose·jepack
JMchen1234 天前
Jetpack 内核实战(十):实战(下)——编辑页、全局设置与内存泄漏排查
kotlin·内存泄漏·android 实战·datastore 设置·jetpack 项目优化·编辑页开发
雨白4 天前
Kotlin 泛型基础:“万能魔法盒”的诞生
kotlin