学不会协程?很可能因为你看过的教程都是错的——Kotlin 的协程「用力瞥一眼」https://www.bilibili.com/video/BV164411C7FK?t=613
Kotlin 协程的挂起好神奇好难懂?今天我把它的皮给扒了 https://www.bilibili.com/video/BV1KJ41137E9?t=132
到底什么是「非阻塞式」挂起?协程真的比线程更轻量级吗?https://www.bilibili.com/video/BV1JE411R7hp
Kotlin Jetpack 实战 | 09. 图解协程:suspend https://juejin.im/post/6883652600462327821
协程
- Kotlin 协程,不是操作系统级别的概念,无需操作系统支持
- Kotlin 协程,有点像上面提到的“绿色线程”,一个线程上可以运行成千上万个协程
- Kotlin 协程,是用户态的(userlevel),内核对协程无感知
- Kotlin 协程,是协作式的,由开发者管理,不需要操作系统进行调度和切换,也没有抢占式的消耗,因此它更加高效
- Kotlin 协程,它底层基于状态机实现,多协程之间共用一个实例,资源开销极小,因此它更加轻量
- Kotlin 协程,本质还是运行于线程之上,它通过协程调度器,可以运行到不同的线程上
有的人会将协程比喻成:线程的封装框架。从宏观角度看,这十分贴切。
高效和轻量,都不是 Kotlin 协程的核心竞争力。
Kotlin 协程的核心竞争力在于:它能简化异步并发
任务。
从上面的动画,我们能知道:
- 表面上看起来是同步的代码,实际上也涉及到了线程切换。
- 一行代码,切换了两个线程。
=
左边:主线程=
右边:IO线程- 每一次从
主线程
到IO线程
,都是一次协程挂起
(suspend) - 每一次从
IO线程
到主线程
,都是一次协程恢复
(resume)。 - 挂起和恢复,这是挂起函数特有的能力,普通函数是不具备的。
- 挂起,只是将程序执行流程转移到了其他线程,主线程并未被阻塞。
- 如果以上代码运行在 Android 系统,我们的 App 是仍然可以响应用户的操作的,主线程并不繁忙,这也很容易理解。
suspend
的本质,就是 CallBack
。
suspend fun getUserInfo(): String {
withContext(Dispatchers.IO) {
delay(1000L)
}
return "BoyCoder"
}
有的小伙伴要问了,哪来的 CallBack
?明明没有啊。确实,我们写出来的代码没有 CallBack,但 Kotlin 的编译器检测到 suspend
关键字修饰的函数以后,会自动将挂起函数转换成带有 CallBack 的函数。
如果我们将上面的挂起函数反编译成 Java,结果会是这样:
// Continuation 等价于 CallBack
// ↓
public static final Object getUserInfo(Continuation $completion) {
...
return "BoyCoder";
}
以上这个从挂起函数
转换成CallBack 函数
的过程,被称为:CPS 转换(Continuation-Passing-Style Transformation)。
小结
- suspend 修饰的函数就是
挂起函数
- 挂起函数,在执行的时候并不一定都会挂起
- 挂起函数只能在其他挂起函数中被调用
- 挂起函数里包含其他挂起函数的时候,它才会真正被挂起
首先,我们只需要把握住 Continuation 的词源 Continue
即可。Continue 是继续
的意思,Continuation 则是继续下去要做的事情,接下来要做的事情</