Kotlin 协程简介
- 协程是一种并发设计模式,可以在 Android 中可以使用它来简化异步执行的代码,协程有助于管理长时间运行的任务,能够用它编写出更清晰、更简洁的代码(官方)
- 协程可以看成是一种轻量级的线程,协程跑在线程中,线程跑在进程中,进程跑在操作系统中,协程的一个典型的使用场景就是线程切换控制
- 协程是 Android 种进行异步编程的推荐解决方案,协程设计的初衷是为了解决并发问题,让协作式多任务的代码实现起来更加方便和直观(用同步的代码风格方式写异步的代码)
- 协程就是 Kotlin 提供的一套线程封装的 API 框架(协程就是个线程框架,从设计思想上来说就是一个基于线程的上层框架),能够让原本比较复杂并行任务的逻辑代码,通过协程改写成结构比较清晰的代码
- 协程优势是挂起时不需要阻塞协程运行所在的线程,从而能够保证主线程安全
- 英文中 Coroutines 和 Threads 就是两个概念,引申出官方采用是 Dispatchers.Main 而不是 Threads.Main 等
- 协程可以利用作用域可以减少内存泄漏,也支持取消操作
suspend 关键字
- 暂停、可挂起(挂起意味着就是需要切线程了,能切走也能切回来)
- 挂起的是协程,不是挂起线程,不是挂起函数
- 利用协程在常规函数的基础上添加了两项操作,用于处理耗时任务。在普通函数 invoke(或者叫 call)和 return 两项操作之外,再添加了 suspend 暂停协程和 resume 恢复协程两项操作,suspend 用于暂停执行当前协程,并保存所有局部变量(Kotlin 使用堆栈帧管理),resume 用于让已挂起的协程从挂起处继续执行
- 挂起函数在执行完成之后,协程会重新切回它原先的线程而这个恢复功能是来自于协程的能力,既然依靠的是协程,那么就必须要在协程中调用(直接或间接)——所以,要求 suspend 函数只能在协程里或者另一个 suspend 函数里被调用(就是所有 suspend 函数都必须在协程中执行),目的就是让协程能够在 suspend 函数切换线程之后再切回来(协程的挂起本质就是线程切出去再切回来)
- suspend 这个关键字本身无法做到挂起,它只是一个标记、约定、显式声明,挂起的操作(切线程)这个逻辑依赖的是挂起函数里面的实际代码来进行的,比如 withContext
- 所以文件的读写、网络的请求、图像的渲染等等耗时的操作就可以写到 suspend 标记的函数里,然后代码写上切线程的逻辑,然后函数拿来供外部协程调用
非阻塞式挂起
-
挂起:可以看成是 暂停协程 和 协程恢复 两个操作之和
-
非阻塞式:假设 launch 创建启动一个父协程,然后里面的代码执行到 suspend 标记的函数了,那此时协程相当于被挂起切到子线程去 suspend 标记的耗时任务了,但不阻塞父协程所在的线程,执行完就自动切回来(当然需要 suspend 函数里的代码里声明好)
ps:针对协程,从个人理解角度看,协程比较不容易懂,我觉得是原先我们的代码编写经验让我们先入为主了,做个假设,如果开发安卓一开始作耗时操作就既定有协程的概念,而不是 Callback 这类概念,那我们可能不会觉得这么奇怪了,因为历史原因引发、导致, Callback 这类概念太深入人心了;—— 再不准确的引申更大一点,为啥安卓等软件开发需要开发者处理主线程、子线程等等呢?而不是它本身直接处理好这里固有的逻辑呢?假设开发者只要标记某某操作是耗时的,可能是优先的,可能是不需要优先的,某某操作是不耗时的等等业务功能的诉求本身的声明即可,具体的实现全隐藏起来了,开发者无需关心,那岂不是很爽?