0
点赞
收藏
分享

微信扫一扫

Coroutine 基本原理,kotlin中文

悄然丝语 2022-02-17 阅读 83

安卓开发中,主线程Main正在执行任务,执行到一个网络请求N(挂起函数)时,N脱离主线程,到指定的IO线程做网络请求。 然后线程Main将继续渲染界面(比如转圈圈loading),等N在IO线程执行结束后,切回Main线程,拿着刚请求完的值,继续做后序操作(比如loading结束,显示请求回来的数据结果)

挂起函数问题

  1. 挂起的对象是协程
  2. 挂起函数只能在另一个挂起函数或者协程中被调用:why?
    因为,切走再恢复(resume)回来是协程的东西,所以只能在协程中调用

原理

好了,现在知道什么是suspend函数了,但是具体是实现或者它原理是什么样的呢?我们来慢慢捋一捋。
挂起函数就是在普通的函数前面加一个关键字suspend,然而Java 平台并没有 suspend 关键字,也没有 suspending 机制,那kotlin又是怎么实现了可以用看起来阻塞的方式实现不阻塞的代码的呢?

CPS 变换

Kotlin 编译器会对 suspending 方法做特殊处理,对代码进行转换,从而实现 suspending 机制。
那 Kotlin 编译器做了哪些处理?简单说,主要做了下面这三项处理:

处理一:增加 Continuation 类型入参,返回值变为 Any?
处理二:生成 Continuation 类型的匿名内部类
处理三:对 suspending 方法的调用变为 switch 形式的状态机
以上引用于作者: 编走编想

那我们来看看最根本的变化:

//挂起函数的函数签名
suspend fun CompletableFuture.await():

变化后

fun CompletableFuture.await(continuation: Continuation): Any?

可以看到,编译器做了什么? 传入了一个Continuation并且返回类型变为了Any?

  • Continuation是什么?

一个接口:Kotlin 的接口可以既包含抽象方法的声明也包含实现。与抽象类不同的是,接口无法保存状态。它可以有属性但必须声明为抽象或提供访问器实现。 (Kotlin interface官方解释)

interface Continuation {
val context: CoroutineContext
fun resumeWith(result: Result)

  • Any?是什么? 表示返回的类型可以是任意类型。

那这里就有一个问题,比如一个函数,原本返回的是String, 为什么这里变成Any了?? 因为编译以后,除了要返回原本要返回的值,还要返回一个标记——COROUTINE_SUSPENDED,所以找不到一个对应的类型来和返回结果匹配,因此变成类一个Any?类型。 ?表示可以为空。

讲原理以前我们先看看怎么写回调: 正常打印一个6

private fun printSix() {
println(“6”)
}

Kotlin中用回调实现:向函数中传入一个Printer,并且把具体的实现方式暴露给调用这个方法的人,

interface Printer{
fun print()
}

private fun printSix(printer: Printer) {
printer.print()
}

fun callPrintSix() {
printSix(object : Printer {
override fun print() {
//具体的实现暴露给调用者
println(“6”)
Log.i(“tag”, “6”)

}
})
}

那么知道Kotlin怎么写回调以后,再回过头看看我们suspend函数的变化:

suspend fun getCard(cardNum : String) {
val card = api.getCard(cardNum)
}

经过编译器以后:

interface Continuation {
val context: CoroutineContext
fun resumeWith(result: Result)
}

fun getCard(cardNum: String,continuation: Continuation) {
//new一个continuation
val newContinuation = object: Continuation({
override resumeWith(…) {
getCard(this) //将new出来的这个newContinuation传入getCard函数自己
//这里看出来了吗,其实就很像是一个递归
}
})
//状态机
switch (newContinuation.label) {
case 0:
newContinuation.cardNum = cardNum
newContinuation.label = 1
return api.getCard(cardNum, newContinuation)
case 2:
val card = newContinuation.result
return card
}
}

最后

如果你看到了这里,觉得文章写得不错就给个赞呗?如果你觉得那里值得改进的,请给我留言。一定会认真查询,修正不足。谢谢。

需要资料的朋友可以点击我的GitHub免费领取

8F%91%E4%B8%8D%E4%BC%9A%E8%BF%99%E4%BA%9B%EF%BC%9F%E5%A6%82%E4%BD%95%E9%9D%A2%E8%AF%95%E6%8B%BF%E9%AB%98%E8%96%AA%EF%BC%81.md)

举报

相关推荐

0 条评论