导图社区 协程基本概念
以下介绍了kotlin协程的基本概念,包含协程是什么、好处、什么时候用协程、协程挂起、协程构建器、结构化并发、CoroutineScope等。
编辑于2021-12-30 15:25:23Kotlin-协程
1. 协程是什么
在线程的基础上封装的api,类似于RxJava,只不过能够以同步的方式调用异步代码
2. 好处
写异步代码简单、易懂
内存泄漏更少
使用结构化并发
易于扩展
3. 什么时候用协程
io操作时
访问文件系统,对文件的读写
发起网络请求
读写数据库
耗时任务
图片的编解码、模糊、美化处理
视频的编解码
其他耗时任务
延时任务、定时器
4. 协程挂起
suspend functions只能在协程或者suspend functions 中被调用。
可以让函数体内的代码在指定的线程中(比如 withContext指定的io线程)运行,如果没有指定线程,那么它将在调用它的线程中运行。
挂起函数之后的代码将被暂停执行。
当前线程会去执行其他的任务,如果是异步线程且没有任务可执行则释放到线程池。
当挂起函数执行完毕,自动切回到(resume)当前线程继续执行挂起函数之后的代码。
一般情况下恢复执行时会自动切回当前线程,除非你的挂起函数使用的Dispatchers是Dispatchers.Unconfined。
5. CoroutineScope
协程作用域包含 CoroutineContext,用于启动协程,并追踪子协程,其实是通过Job追踪的。
所有的协程必须运行在CoroutineScope中。
常用的创建方式
CoroutineScope(context: CoroutineContext)
val scope = CoroutineScope(Dispatchers.Main + Job())
class LifecycleCoroutineScope : CoroutineScope, Closeable { private val job = JobSupervisorJob() override val coroutineContext: CoroutineContext get() = Dispatchers.Main + job override fun close() { job.cancel() } }
class SimpleRetrofitActivity : FragmentActivity() { private val activityScope = LifecycleCoroutineScope() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_simple_retrofit) // some other code ... } override fun onDestroy() { super.onDestroy() activityScope.close() } // some other code ... }
coroutineScope:api方法,创建新一个子域,并管理域中的所有协程。
这个方法只有在block中创建的所有子协程全部执行完毕后,才会退出。
supervisorScope
与 coroutineScope的区别是在子协程失败时,错误不会往上传递给父域,所以不会影响子协程。
根job取消时将所有job全部取消。
也会在所有的子job执行结束前等待, 就像 coroutineScope 所做的那样。
6. CoroutineDispatcher
协程调度器,决定协程所在的线程或线程池。它可以指定协程运行于特定的一个线程、一个线程池或者不指定任何线程。
Dispatchers.Main
主线程,和UI交互,执行轻量任务
Dispatchers.IO
用于网络请求和文件访问
Database
Reading/writing files
Networking
Dispatchers.Default
CPU密集型任务
Parsing JSON
延时任务
定时器
图片的编解码、模糊、美化处理
视频的编解码
耗时循环
Dispatchers.Unconfined
调用它的线程启动了一个协程,但它仅仅只是运行到第一个挂起点,即挂起函数前的代码运行在调用它的线程中。
挂起函数resume后的代码运行在和挂起函数同一个线程中
7. CoroutineContext
协程上下文,主要包含Job和CoroutineDispatcher,表示一个协程的场景。
Dispatchers.Default + CoroutineName("test")
组合上下文中的元素
8. Job
任务,封装了协程中需要执行的代码逻辑。
Job 可以取消并且有简单生命周期,它有三种状态:isActive、isCompleted、isCancelled。
join()
等待直到这个被join的job执行结束
cancle()
所有的挂起函数都是可被取消的。它们检查协程的取消, 并在取消时抛出 CancellationException。
你可以try catch 捕获到 CancellationException,但是状态依然会变成取消状态
CancellationException 不会取消它的父协程
取消是协作的
如果协程正在执行计算任务,并且没有检查取消的话,那么它是不能被取消的
CoroutineScope.isActive
通过isActive判断是否被取消
cancelAndJoin()
取消这个任务,并等待它完成
Deferred
Job的子类,有返回值的Job
await
等待job执行结束,并获取返回的结果
SupervisorJob
继承至CompletableJob,CompletableJob也继承至Job
子协程的异常和取消不会往上传播
SupervisorJob 的取消只会向下传播
子job的异常可以通过CoroutineExceptionHandler捕获
9. 协程构建器
lauch()
创建并启动(也可以延时启动)一个协程,返回一个Job,用于监督和取消任务,用于无返回值的场景。
接受一个可选的CoroutineContext,用来指定一个调度器
当不传参数时,从启动它的CoroutineScope中承袭了上下文以及调度器。
async()
和launch一样,区别是返回一个Job的子类 Deferred,唯一的区别是可以通过await获取完成时的返回值,或者捕获异常(异常处理也不一样)。
用于实现并发
10. 异常
两种形式的协程构建器
自动传播异常
launch
actor
当这些构建器用于创建一个根协程时
未捕获异常,类似 Java 的 Thread.uncaughtExceptionHandler
向用户暴露异常
async
produce
当这些构建器用于创建一个根协程时
依赖用户来最终消费异常:通过 await 或 receive
try/catch
无法捕捉launch { } 作用域的异常
能够捕获async启动的协程的异常
CoroutineExceptionHandler
只有作为最外层的父协程上下文才有效, 因为异常会层层上抛
除非配合SupervisorJob监督作业禁止异常上抛, 子作用域的异常处理器才能捕获到异常
async 构建器始终会捕获所有异常并将其表示在结果 Deferred 对象中, 因此它的 CoroutineExceptionHandler 也无效。
异常聚合
当协程的多个子协程因异常而失败时, 一般规则是“取第一个异常”,因此将处理第一个异常。
在第一个异常之后发生的所有其他异常都作为被抑制的异常绑定至第一个异常。
exception.suppressed
11. 结构化并发
取消任务
当协程不再需要的时候
可以通过job取消
追踪任务
当协程运行的时候
通过CoroutineScope保证
外部协程会等待,直到在其作用域中启动的所有协程都执行完毕后才会结束。
传播错误信号
当协程执行失败的时候
coroutineScope 保证错误双向传递,只要有一个子coroutine失败或出现异常,异常往父域传递,并取消所有的子coroutines。
supervisorScope 实现单向错误传递,适用于作业监控器。