kotlin-协程
本文最后更新于 664 天前
1. 基本认识

Kotlin 的编译器检测到 suspend 关键字修饰的函数以后,会自动将挂起函数转换成带有 CallBack 的函数

suspend fun getUserInfo(): String {
    withContext(Dispatchers.IO) {
        delay(1000L)
    }
    return "BoyCoder"
}


//                              Continuation 等价于 CallBack
//                                         ↓         
public static final Object getUserInfo(Continuation $completion) {
  ...
  return "BoyCoder";
}

重点是这个图片,后面分析挂起函数内多个挂起函数,理解实现状态机切换相关回调

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

2. 反编译后分析
suspend fun testCoroutine() {
    log("start")
    val user = getUserInfo()
    log(user)
    val friendList = getFriendList(user)
    log(friendList)
    val feedList = getFeedList(friendList)
    log(feedList)
}

// 没了 suspend,多了 completion
fun testCoroutine(completion: Continuation<Any?>): Any? {}

// 注意上图 表示 completion对应的是什么,实现 invoke回调 从而执行后续的 挂起函数
fun getUserInfo(completion: Continuation<Any?>): Any?{}
fun getFriendList(user: String, completion: Continuation<Any?>): Any?{}
fun getFeedList(friendList: String, completion: Continuation<Any?>): Any?{}

在 testCoroutine 函数里,会多出一个 ContinuationImpl 的子类,它的是整个协程挂起函数的核心

fun testCoroutine(completion: Continuation<Any?>): Any? {
    // 初始化判断是否有 ‘回调’实例,防止重复创建
    val continuation = if (completion is TestContinuation) {
        completion
    } else {
        //                作为参数
        //                   ↓
        TestContinuation(completion)
    }


    // completion参数  对应👆说的那个图
    class TestContinuation(completion: Continuation<Any?>?) : ContinuationImpl(completion) {
        // 表示协程状态机当前的状态
        var label: Int = 0
        // 协程返回结果
        var result: Any? = null

        // 用于保存之前协程的计算结果
        var mUser: Any? = null
        var mFriendList: Any? = null

        // invokeSuspend 是协程的关键
        // 它最终会调用 testCoroutine(this) 开启协程状态机
        // 状态机相关代码就是后面的 when 语句
        // 协程的本质,可以说就是 CPS + 状态机
        override fun invokeSuspend(_result: Result<Any?>): Any? {
            result = _result
            label = label or Int.Companion.MIN_VALUE
            return testCoroutine(this)
        }
    }

    // 三个变量,对应原函数的三个变量
    lateinit var user: String
    lateinit var friendList: String
    lateinit var feedList: String

    // result 接收协程的运行结果
    var result = continuation.result

    // suspendReturn 接收挂起函数的返回值
    var suspendReturn: Any? = null

    // CoroutineSingletons 是个枚举类
    // COROUTINE_SUSPENDED 代表当前函数被挂起了
    val sFlag = CoroutineSingletons.COROUTINE_SUSPENDED

    when (continuation.label) {
        0 -> {
            // 检测异常
            throwOnFailure(result)

            log("start")
            // 将 label 置为 1,准备进入下一次状态
            continuation.label = 1

            // 执行 getUserInfo
            suspendReturn = getUserInfo(continuation)

            // 判断是否挂起
            if (suspendReturn == sFlag) {
                return suspendReturn
            } else {
                result = suspendReturn
                //go to next state  会使用到label相关的 goto语法,仅仅使用suspend进行修饰,并没有相应的协程域-“伪挂起函数” 会走到这里
            }
        }

        1 -> {
            throwOnFailure(result)

            // 获取 user 值
            user = result as String
            log(user)
            // 将协程结果存到 continuation 里
            continuation.mUser = user
            // 准备进入下一个状态
            continuation.label = 2

            // 执行 getFriendList
            suspendReturn = getFriendList(user, continuation)

            // 判断是否挂起
            if (suspendReturn == sFlag) {
                return suspendReturn
            } else {
                result = suspendReturn
                //go to next state
            }
        }

        2 -> {
            throwOnFailure(result)

            user = continuation.mUser as String

            // 获取 friendList 的值
            friendList = result as String
            log(friendList)

            // 将协程结果存到 continuation 里
            continuation.mUser = user
            continuation.mFriendList = friendList

            // 准备进入下一个状态
            continuation.label = 3

            // 执行 getFeedList
            suspendReturn = getFeedList(friendList, continuation)

            // 判断是否挂起
            if (suspendReturn == sFlag) {
                return suspendReturn
            } else {
                result = suspendReturn
                //go to next state
            }
        }

        3 -> {
            throwOnFailure(result)

            user = continuation.mUser as String
            friendList = continuation.mFriendList as String
            feedList = continuation.result as String
            log(feedList)
            loop = false
        }
    }
}
  • when 表达式实现了协程状态机
  • continuation.label 是状态流转的关键
  • continuation.label 改变一次,就代表协程切换了一次
  • 每次协程切换后,都会检查是否发生异常
  • testCoroutine 里的原本的代码,被拆分到状态机里各个状态中,分开执行
  • getUserInfo(continuation),getFriendList(user, continuation),getFeedList(friendList, continuation) 三个函数调用传的同一个 continuation 实例。
  • 一个函数如果被挂起了,它的返回值会是:CoroutineSingletons.COROUTINE_SUSPENDED
  • 切换协程之前,状态机会把之前的结果以成员变量的方式保存在 continuation 中。

以下代码为伪代码

iichen:https://iichen.cn/?p=409
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇