Kotlin 协程源代码泛读:Continuation 思想实验

考虑这样一个故事(场景),我们要从网络上下载图片并展示

kotlin 复制代码
fun display(url: String) {
    val data = download(url)
    val image = decode(data)
    show(image)
}

download 和 decode 是耗时操作,不能在 Ui 线程中执行

所以修改 download 和 decode,让它们异步工作,完成后通过 callback 回调

这里特意把 callback 定义成和 Kotlin 中同样的名字 Continuation

kotlin 复制代码
fun display(url: String) {
    download(url) {
        decode(image) {
            show(image)
        }
    }
}

fun download(url: String, continuation: Continuation) {}
fun decode(data: ByteArray, continuation: Continuation) {}

我们优化一下上面这种回调写法,实际上也是 Kotlin 协程框架采用的方案:

kotlin 复制代码
fun display(url: String) {
    val continuation = object: Continuation {
        var label = 0
        var result: Any? = null
        fun resumeWith(result: Any) {
        }
    }
    when (continuation.label) {
        0 -> {
            continuation.label = 1
            download(url, continuation)
            return
        }

        1 -> {
            continuation.label = 2
            decode(continuation.result as ByteArray, continuation)
            return
        }
    }
    display(continuation.result as Image)
}
  • continuation 的 label 字段标识不同的阶段
  • when 执行display 函数不同的阶段/部分
  • download 和 decode 执行完毕后调用 continuaton 的 resumeWith 方法通知调用者

这里有个问题,resumeWith 咋实现? 我们需要修改方案吗?比如把 when代码快挪到 resumeWith 里头? Kotlin 协程框架采用的方法是,给 display 增加一个 continuation 参数,然后递归调用!

kotlin 复制代码
fun display(url: String, continuation: Continuation? = null) {
    // 这里有个调用约定,开始时 continuation 为 null
    val continuation = continuation ?: object: Continuation {
        var label = 0
        var result: Any? = null
        fun resumeWith(result: Any) {
            continuation.result = result
            // 递归调用
            display(url, continuation)
        }
    }

    ...
}

当然这个 continuation 参数另有作用!

扩展一下这个场景,display 后,我们想在 display 之后干点什么!

你肯定会想那简单啊,我们已经有 continuation了!

kotlin 复制代码
fun page() {
    val continuation = object: Continuation {
        fun rewumeWith(result: Any?) {
            // 干点什么~
        }
    }
    display(url, continuation)
}

问题又来了,之前我们约定调用 display 作为入口调用的时候 continuation 需要传 null...

这里也不卖关子了,Kotlint 的解决方案是类型判断!

kotlin 复制代码
fun display(url: String, completion: Continuation? = null) {
    var $continuation: Continuation? = null
    if (continuation is ContinuationImpl) {
        // 自己人!(递归调用) 
    } else {
        // 异乡人!,注意这里保存了 completion
        $continuation = ContinuationImpl(completion)
    }
   // 定义一个内部类
   inner class ContinuationImpl(
       completion: Continuation
   ): Continuation {
       fun resumeWith(result: Any?) {
           ...
       }
   }
   ...
   show(...)
   continuation?.resumeWith(...)
}

好了,故事讲完了,回到现实中

这个故事的 suspend 函数版本如下

kotlin 复制代码
suspend fun display(url: String) {
    val data = download()
    val image = decode(data)
    show(image)
}

suspend fun download(url: String) {}
suspend fun decode(data: ByteArray) {}

感谢 Kotlin 协程~

相关推荐
AI工具测评与分析29 分钟前
EhViewer安卓ios全版本类下载安装工具的完整路径解析
android·ios
非凡ghost1 小时前
Control Center 安卓版:个性化手机控制中心
android·智能手机·生活·软件需求
撩得Android一次心动2 小时前
Android 项目:画图白板APP开发(一)——曲线优化、颜色、粗细、透明度
android
月夜风雨磊10 小时前
Android NDK从r10c版本到r29版本的下载链接
android·gitee·android ndk
louisgeek10 小时前
Android MIUI 开启 Google Play 服务
android
独行soc11 小时前
2025年大模型安全岗的面试汇总(题目+回答)
android·人工智能·安全·面试·职场和发展·渗透测试
前行的小黑炭12 小时前
Android App:每次想写新项目锻炼一下,但苦于没有UI,那么这篇文章适合你~(适合基础小白锻炼)
android·kotlin
雨白14 小时前
压缩、序列化与哈希
android
安卓开发者15 小时前
RxJava 核心概念解析:构建响应式Android应用的基石
android·echarts·rxjava
叽哥15 小时前
flutter学习第 18 节:设备功能调用
android·flutter·ios