朱涛 · Kotlin 编程第一课
朱涛
Google 认证的 Kotlin、Android 开发者专家,博客“Kotlin Jetpack 实战”作者
6717 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 50 讲
朱涛 · Kotlin 编程第一课
15
15
1.0x
00:00/00:00
登录|注册

15 | 挂起函数:Kotlin协程的核心

你好,我是朱涛。这节课,我们来学习协程的挂起函数。
挂起函数,是 Kotlin 协程当中最基础、最重要的知识点。如果对协程的挂起函数没有足够的认识,我们后续的学习将会困难重重。如果不理解挂起函数,我们将无法理解协程的非阻塞;如果不了解挂起函数,我们将无法掌握 Channel、Flow 等 API;如果不理解挂起函数,我们写出来的代码也会漏洞百出,就更别提优化软件架构了。
相反,如果能将挂起函数理解透彻,我们后面的学习也会更加轻松一些。所以这节课,我会从应用和原理两个角度,来带你理解挂起函数,包括如何使用挂起函数来优化异步任务,以及挂起函数的 CPS 当中的 Continuation 到底是什么。通过对这两个维度的学习,你在更轻易地掌握挂起函数应用场景的同时,对它的底层原理也会有一定认识。
那么接下来,你一定要打起精神,我们一起来攻克这个关键的知识点!

挂起函数:Kotlin 协程的优势

通过前面课程的学习,我们已经知道了:协程就像是轻量级的线程一样。用线程能实现的功能,我们借助 launch 和 async 也同样可以做到。
不过你可能会好奇,如果只是把 thread{} 替换成 launch{},那协程比起线程也没什么特殊的优势吧?仅仅只是因为“轻量”“非阻塞”,我们就应该放弃线程,拥抱协程吗?
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

Kotlin协程的核心特性是挂起函数,它通过suspend关键字实现非阻塞的异步操作,使得异步编程更加简洁和易于维护。挂起函数的本质是Callback,通过CPS转换实现挂起和恢复。文章深入浅出地解释了挂起函数和协程的关系,为读者提供了对Kotlin协程的深入了解和学习方向。挂起函数的类型变化和Continuation的概念解释了其实现原理。此外,文章还探索了挂起函数在协程中的调用关系,强调了挂起函数的特殊性。通过实例代码和动画形象地展示了挂起函数的执行流程,帮助读者深入理解其背后的细节。总之,本文全面介绍了Kotlin协程中挂起函数的重要性和实现原理,为读者提供了深入学习的指导方向。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《朱涛 · Kotlin 编程第一课》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(29)

  • 最新
  • 精选
  • $Kotlin
    public interface Continuation<in T> { /** * The context of the coroutine that corresponds to this continuation. */ public val context: CoroutineContext /** * Resumes the execution of the corresponding coroutine passing a successful or failed [result] as the * return value of the last suspension point. */ public fun resumeWith(result: Result<T>) } suspend函数的入参Continuation,看源码可以知道需要有一个协程上下文CoroutineContext信息,只有在协程作用域里才能传递。

    作者回复: 很棒的答案。

    2022-02-16
    2
    21
  • Airsaid
    老师您好,为什么 Kotlin 选择使用关键字来定义挂起函数而不是使用注解呢?(例如 Compose 就使用的是注解的方式)

    作者回复: 首先,关键字其实是更好的方案,因为它是语法级的支持,注解不是。关键字是语法的一部分,而注解只是额外标注的信息,这在编译器分析抽象语法树的时候,两者的差异是很大的。 Compose之所以使用注解,是因为Compose没有增加关键字的权限,只能通过编译器插件来实现内部逻辑。

    2022-02-28
    20
  • 墨方
    被调用的挂起函数需要传入一个Continuation(当然这个传入也是幕后编译做的), 没有被suspend修饰的函数是没有Continuation参数的,所以被调用的挂起函数没有办法从普通函数中获取一个Continuation。

    作者回复: 很棒的答案!

    2022-02-16
    18
  • colin
    挂起函数本身并不支持挂起,所以它没法在普通函数中调用,而它之所以能在挂起函数中调用,是因为挂起函数最终都是在协程中被调用,是协程提供了挂起函数运行的环境。

    作者回复: 没错,具体的运行环境,就是Continuation还有上下文环境CoroutineContext。

    2022-02-16
    2
    6
  • AKEI
    所以kotlin的挂起函数只是相当于让回调函数更简洁,相当于封装了线程池,并没有任何更高效的性能优化是吗?

    作者回复: 它有非阻塞的特性,另外,它还更轻量,在特定场景下是会更好的。

    2022-05-15
    4
  • Luckykelan
    老师您好,有个问题不太清楚 val user = getUserInfo() val friendList = getFriendList(user) val feedList = getFeedList(friendList) 这段代码和协程思维模型那张动图一起看,代码执行到getUserInfo()函数时,这个函数就被挂起了,不是应该继续执行val friendList = getFriendList(user)吗?为什么实际上在这里没有继续执行而是等待了getUserInfo()的返回呢?

    作者回复: 这里的挂起,有两层含义: 1. 一个是当前线程不会被阻塞,可以执行其他任务。 2.挂起点剩下的代码,会留到之后再执行。

    2022-04-23
    7
    3
  • 的的喀喀湖
    讲的确实不错,之前看了好多文章没看懂挂起的概念,跟着这两篇文章自己走了一遍代码终于能理解了

    作者回复: 加油~

    2022-02-27
    3
  • jim
    图文说明,写的真好。

    作者回复: :)

    2022-02-21
    2
  • InfoQ_0880b52232bf
    关于思考题,我想可以尝试逆向思考一下,假如普通函数可以调用挂起函数,那么会出现什么情况呢? 比如:我们在main方法里可以直接调用这三个挂起函数(实际不能直接调用),我们预期的结果是同步方式实现异步请求(这也是协程的特点之一),但其实按照非阻塞挂起的特点,main方法会直接打印“main end”,无法满足我们的预期: fun main() { val userInfo = getUserInfo() val friendList = getFeedList(userInfo) val feedList = getFeedList(friendList) println("main end") } suspend fun getUserInfo(): String { withContext(Dispatchers.IO) { delay(1000L) } return "BoyCoder" } suspend fun getFriendList(user: String): String { withContext(Dispatchers.IO) { delay(1000L) } return "Tom, Jack" } suspend fun getFeedList(list: String): String { withContext(Dispatchers.IO) { delay(1000L) } return "{FeedList..}" }

    作者回复: 不错的角度,有点反证法的意思~

    2022-04-18
    1
  • Paul Shan
    suspend -> Continuation ->CoroutineContext + resumeWith 协程上下文才是挂起和回调的幕后黑手,😀,也就是说所有的挂起函数调用的时候最终都要依托于某个协程。

    作者回复: 可以这么理解。

    2022-03-22
    1
收起评论
显示
设置
留言
29
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部