课程名称: 一课掌握Kotlin 突破开发语言瓶颈
课程章节: 协程初步 - Kotlin协程的基本要素(11-5,11-6)
课程讲师: bennyhuo
课程内容:
挂起函数
- 挂起函数就是用suspend修饰的函数
- 挂起函数只能在其他挂起函数或协程中调用
- 挂起函数调用时包含了协程“挂起”的语义
- 挂起函数返回时包含了协程“恢复”的语义
挂起函数的类型
suspend fun foo() {} ---- suspend () -> Unit
suspend fun foo(a: Int): String {} ---- suspend (Int) -> String
跟函数类型一致,只是前面多了个suspend关键字修饰
挂起函数的本质
suspend修饰的挂起方法,通过Java反编译或者在Java上调用时,挂起函数会多了一个参数Continuation
fun foo(continuation: Continuation<Unit>): Any {}
fun foo(a: Int, continuation: Continuation<String>): Any {}
- Continuation的泛型参数由suspend挂起函数的返回值决定
- 由于挂起函数实际上需要接收一个Continuation参数,所以挂起函数只能在其他挂起函数或协程中调用
- Any的返回值有两种情况:
- 当挂起函数里面没有真正的挂起,那么Any就是函数的返回值
- 当挂起函数里面真正的挂起,那么Any返回的是挂起标志对象(COROUTINE_SUSPEND)
什么是真正的挂起和没有真正的挂起?
真正的挂起就是必须异步调用resume
比如:
- 切换到其他线程resume
- 单线程事件循环异步执行
没有真正的挂起就是在suspendCoroutine中直接return,或者没有进行线程切换就执行resume
如何将一个回调函数转写成挂起函数
- 使用suspendCoroutine获取挂起函数的Continuation
- 回调成功的分支使用Continuation.resume(value)
- 回调失败则使用Continuation.resumeWithException(e)
协程的创建和启动
createCoroutine
createCoroutine是先创建出一个协程Continuation,然后在通过Continuation.resume启动协程
创建协程:
fun <T> (suspend () -> T).createCoroutine(
completion: Continuation<T>
): Continuation<Unit>
fun <R, T> (suspend R.() -> T).createCoroutine(
receiver: R,
completion: Continuation<T>
): Continuation<Unit>
创建协程涉及到两个Continuation:
- 一个作为createCoroutine的参数穿进去,给挂起函数执行完后调用
- 一个作为createCoroutine的返回值,作为该协程的句柄,用于启动协程
启动协程
调用resume(Unit)
suspend {
...
}.createCoroutine(object: Continuation<Unit> {
override val context = EmptyCoroutineContext
override fun resumeWith(result: Result<Unit>) {
log("Coroutine End with $result")
}
}).resume(Unit)
startCoroutine
startCoroutine是创建出协程后立马调用
fun <T> (suspend () -> T).startCoroutine(
completion: Continuation<T>
)
fun <R, T> (suspend R.() -> T).startCoroutine(
receiver: R,
completion: Continuation<T>
)
创建和启动协程
suspend {
...
}.startCoroutine(object: Continuation<Unit> {
override val context = EmptyCoroutineContext
override fun resumeWith(result: Result<Unit>) {
log("Coroutine End with $result")
}
})
协程上下文
suspend {
...
}.startCoroutine(object: Continuation<Unit> {
override val context = EmptyCoroutineContext
...
})
- 协程执行过程中需要携带数据
- 索引是CoroutineContext.Key
- 元素是CoroutineContext.Element
拦截器
- 拦截器ContinutationInterceptor是一类协程上下文元素
- 可以对协程上下文所在协程的Continuation进行拦截
interface ContinuationInterceptor: CoroutineContext.Element {
fun <T> interceptContinuation(
continuation: Continuation<T>
): Continuation<T>
}
Continuation执行示意
suspend {
...
}.startCoroutine(object: Continuation<Unit> {
override val context = EmptyCoroutineContext
override fun resumeWith(result: Result<Unit>) {
...
}
})
通过createCoroutine或startCoroutine创建出来的类为SuspendLambda,它是Continuation的一个实现类
其中,suspend {…}为协程的本体,是协程真正的实现逻辑
如果SuspendLambda里面有挂起函数的调用,每次调用还会用SafeContinuation对SuspendLambda进行包装
SafeContinuation的作用是:
- 确保resume只被调用一次
- 确保如果在当前线程调用栈上直接调用则不会挂起
SafeContinuation可以理解为一个拦截器,它拦截的就是SuspendLambda
在前面的基础上再加上拦截器
课程收获
Beny老师的讲解非常细致,对协程设计到的要素都有细致的讲解。通过介绍如何将回调改写成挂起函数,是我对挂起函数的本质有更深刻的理解。在基本要素讲解的过程中,也让我对协程的原理有了一定的认识。
由于刚刚接触协程,刚听完还是有些似懂非懂,通过手记的整理,让我对基本要素有了更深刻的认识,后续还得多回顾几遍,争取把协程摸透。
共同学习,写下你的评论
评论加载中...
作者其他优质文章