为了账号安全,请及时绑定邮箱和手机立即绑定

【九月打卡】第1天 一课掌握Kotlin 突破开发语言瓶颈

标签:
Kotlin

课程名称: 一课掌握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的返回值有两种情况:
    1. 当挂起函数里面没有真正的挂起,那么Any就是函数的返回值
    2. 当挂起函数里面真正的挂起,那么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:

  1. 一个作为createCoroutine的参数穿进去,给挂起函数执行完后调用
  2. 一个作为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老师的讲解非常细致,对协程设计到的要素都有细致的讲解。通过介绍如何将回调改写成挂起函数,是我对挂起函数的本质有更深刻的理解。在基本要素讲解的过程中,也让我对协程的原理有了一定的认识。
由于刚刚接触协程,刚听完还是有些似懂非懂,通过手记的整理,让我对基本要素有了更深刻的认识,后续还得多回顾几遍,争取把协程摸透。

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
移动开发工程师
手记
粉丝
4
获赞与收藏
1

关注作者,订阅最新文章

阅读免费教程

  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消