写点什么

Coroutine 基本原理,吃透这份阿里 P8 纯手打 Android 面经

用户头像
Android架构
关注
发布于: 13 小时前
  1. 那我们再用 Kotlin 的协程实现一下呢?


//协程 suspend fun showCard(){val cardDetail = getCard(canId) // 获取卡详情 val sofList = getSofList(canId) // 获取银行卡信息 val cardInfo = suspendMerge(cardDetail.await, sofList.await) // 合并两个信息 show(cardInfo) // UI 显示}


哇塞!是不是炒鸡简洁明了?? 对了,这就是为什么要用 kotlin 协程。这个里面干了啥我们不用管,反正此刻只需要知道,协程就让原本用 callback 写起来很复杂的东西变的整洁干净,清晰明了就好了。

挂起函数(suspend 函数)

刚上面就出现了一个关键字 suspend, 那这个加了 suspend 这个词的函数又是啥呢? -> 挂起函数

那什么是挂起函数?

就是在普通的函数前面加一个关键字 suspend


一般函数写法:


fun getCard(


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


) {......}


挂起函数写法:


suspend fun getCard() {......}

suspend 关键字作用:

提醒 这个函数的调用者: 我是一个耗时的函数, 请在协程中调用我。

挂起函数作用

说了半天,为什么要用挂起函数?


挂起的意思是:暂时从当前线程脱离,一会儿再切/恢复回来(resume)


当一个函数从当前线程 A 挂起后,就出现了两条线:


  1. A 线程:该执行什么执行什么,比如刷新界面,或者没有其他事情可做就被回收再利用

  2. 挂起函数:切换到指定线程,并从被挂起的那行代码开始,继续向下执行代码,执行完以后,再切回到 A 线程


举个例子吧:


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

挂起函数问题

  1. 挂起的对象是协程

  2. 挂起函数只能在另一个挂起函数或者协程中被调用:why?

  3. 因为,切走再恢复(resume)回来是协程的东西,所以只能在协程中调用

原理

好了,现在知道什么是suspend函数了,但是具体是实现或者它原理是什么样的呢?我们来慢慢捋一捋。


挂起函数就是在普通的函数前面加一个关键字suspend,然而 Java 平台并没有 suspend 关键字,也没有 suspending 机制,那 kotlin 又是怎么实现了可以用看起来阻塞的方式实现不阻塞的代码的呢?

CPS 变换

Kotlin 编译器会对 suspending 方法做特殊处理,对代码进行转换,从而实现 suspending 机制。


那 Kotlin 编译器做了哪些处理?简单说,主要做了下面这三项处理:


处理一:增加 Continuation 类型入参,返回值变为 Any?


处理二:生成 Continuation 类型的匿名内部类


处理三:对 suspending 方法的调用变为 switch 形式的状态机


[以上引用于作者: 编走编想](


)


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


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


变化后


fun <T> CompletableFuture<T>.await(continuation: Continuation<T>): Any?


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


  • Continuation是什么?


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


))


interface Continuation<in T> {val context: CoroutineContextfun resumeWith(result: Result<T>)


  • 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)

用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
Coroutine 基本原理,吃透这份阿里P8纯手打Android面经