多重回调的回调地狱,就像是龟派气功。
饿了么资深 Android 工程师 张涛 如是说。
《领略 Kotlin 协程的力量》的学习笔记。
注:文中示例基于 2018 年的 kotlinx.coroutines 0.x 版本,
CommonPool、launch(UI)等 API 已在 1.0 移除,现在应改用Dispatchers与withContext。
Kotlin 的协程功能确实非常强大,类似 Dart 的 async/await 用法。
什么是协程
-
进程 - 进程间切换要保存状态数据
- 单 CPU 时间分片
- 多 CPU 并行执行
-
线程 - 多线程切换,寄存器保存状态
- 线程阻塞
-
协程概念:通过提升 CPU 利用率,减少线程切换,进而提升 CPU 运行效率
-
协程的特点
- 可控制 - 协程能做到可控地发起子任务
- 轻量级 - 协程非常小,占用资源比线程还少
- 语法糖 - 使多任务或多线程切换不再使用回调方法
如何使用 Kotlin 协程
-
例子:第三方登录,多个顺序接口请求
-
不用协程:需要多重回调,陷入了回调地狱
-
使用协程: runBlocking - T 用于执行协程任务,通常用于启动最外层协程
-
launch:Job 协程内启动协程
fun setText(name: String) = launch(UI){ textView.text = name } -
async/await:Deferred 成对出现,用于执行协程并获取返回值
suspend fun requestToken() = async(CommonPool){ return@async "curl http://www.example.com" }.await() -
suspend关键字 -
suspend修饰的函数(或 lambda),只能被suspend修饰的函数(或 lambda)调用// 编译前 suspend fun requestToken():String {...} // 编译后 Object requestToken(Continuation<String> c) {...} // Continuation接口 interface Continuation<in T> { val context: CoroutineContext fun resume(value: T) // 成功回调 fun resumeWithException(e: Throwable) // 失败回调 }
-
如何在现有的项目中引入协程
-
使用扩展方法,扩展已有方法
interface Service { fun requestToken(): Call<Token> } suspend fun requestToken(): Token = serverInterface.requestToken().await() -
改进 I/O 操作
fun save(textView: TextView, file: File) = runBlocking { val text = textView.text.toString() val deferred = async(CommonPool) { file.appendText(text) file.readText() } deferred.await() } -
引入协程时充分利用协程的特性
-
不要滥用协程