Kotlin 协程总结
协程概念
协程就是一个线程。
协程原理:
https://juejin.cn/post/7212311942613385253#heading-15
速通协程,一步到位!
协程就像非常轻量级的线程。线程是由系统调度的,线程切换或线程阻塞的开销都比较大。而协程依赖于线程,但是协程挂起时不需要阻塞线程,几乎是无代价的,协程是由开发者控制的。所以协程也像用户态的线程,非常轻量级,一个线程中可以创建任意个协程。
协程很重要的一点就是当它挂起的时候,它不会阻塞其他线程。协程底层库也是异步处理阻塞任务,但是这些复杂的操作被底层库封装起来,协程代码的程序流是顺序的,不再需要一堆的回调函数,就像同步代码一样,也便于理解、调试和开发。它是可控的,线程的执行和结束是由操作系统调度的,而协程可以手动控制它的执行和结束。其实就是协程自己加了回调
协程可以理解为协程各自在各自的线程上,且线程不同。其实如果多个协程共用一个线程,其实它们之间也就没有线程并发问题了
为什么需要 Kotlin 协程
提供方便的线程操作API,编写逻辑清晰且简洁的线程代码。
协程是Google在 Android 上进行异步编程的推荐解决方案。具有如下特点:
轻量:您可以在单个线程上运行多个协程,因为协程支持挂起,不会使正在运行协程的线程阻塞。挂起比阻塞节省内存,且支持多个并行操作。
内存泄漏更少:使用结构化并发机制在一个作用域内执行多项操作。
内置取消支持:取消操作会自动在运行中的整个协程层次结构内传播。
Jetpack 集成:许多 Jetpack 库都包含提供全面协程支持的扩展。某些库还提供自己的协程作用域,可供您用于结构化并发
好文章:https://blog.csdn.net/poorkick/article/details/112131961?spm=1001.2014.3001.5501
什么时候用协程
当你需要切线程或者指定线程的时候。你要在后台执行任务
好处:有回调,像同步的方式写异步代码
- 并发实现方便
- 没有回调嵌套发生, 代码结构清晰
suspend
挂起函数在执行完成之后,协程会重新切回它原先的线程。
挂起,挂起函数在执行完成之后一个稍后会被自动切回来的线程调度操作。
代码执行到 suspend 函数的时候会『挂起』,并且这个『挂起』是非阻塞式的,它不会阻塞你当前的线程
「切回来」就类似于协程会帮我再 post 一个 Runnable,让我剩下的代码继续回到主线程去执行。
这个函数实质上并没有发生挂起,那你这个 suspend 关键字只有一个效果:就是限制这个函数只能在协程里被调用,如果在非协程的代码中调用,就会编译不通过。
正确用法:给函数加上 suspend 关键字,然后在 withContext 把函数的内容包住就可以了。
示例
1 |
|
await
await() 不会阻塞主线程,只是会挂起协程,当requestData()执行完返回结果后,processData()的逻辑会恢复执行
withContext
这个函数可以切换到指定的线程,并在闭包内的逻辑执行结束之后,自动把线程切回去继续执行
withContext 会阻塞上下文线程
二种启动
串行并行
Job
Job 是协程的句柄。使用 launch 或 async 创建的每个协程都会返回一个 Job 实例,该实例唯一标识协程并管理其生命周期。您还可以将 Job 传递给 CoroutineScope 以进一步管理其生命周期 (关闭)
SupervisorJob
用 SupervisorJob 替代 Job,SupervisorJob 与 Job 基本类似,区别在于不会被子协程的异常所影响。
处理协程异常
如何优雅的处理协程的异常?
不会影响其他,SupervisorJob 让协程自己处理异常。与之相反的,Job 会传播异常,所以 catch 代码块不会被调用。
1 |
|
Coroutine
Coroutine Scope /ˈkəʊruːˌtiːn/
CoroutineScope是协程作用域,其内部本身就含有一个CoroutineContext线程,默认是主线程。
而CoroutineContext则是在协程作用域中执行的线程切换。
CoroutineScope 会跟踪它使用 launch 或 async 创建的所有协程。您可以随时调用 scope.cancel() 以取消正在进行的工作(即正在运行的协程)。
viewModelScope
1 |
|
Dispatchers
- Dispatchers.Main:Android 中的主线程
- Dispatchers.IO:针对磁盘和网络 IO 进行了优化,适合 IO 密集型的任务,比如:读写文件,操作数据库以及网络请求
- Dispatchers.Default:适合 CPU 密集型的任务,比如计算
不阻塞怎么实现
我们要保证不阻塞,同时又要是同步式写法,该怎样做呢?异步回调callback是一种不阻塞的方式,或许协程底层也是这种实现方式(除此之外我不知道有什么不阻塞的底层实现方式),只不过外层帮我们封装成现在的同步式写法了。
普通接口回调改成协程
在 await 函数的挂起结束以后,它会调用 continuation 参数的 resumeWith 函数,来恢复执行 await 函数后面的代码。