《Android Kotlin协程实战》
https://www.bilibili.com/video/BV1uo4y1y7ZF/?spm_id_from=333.999.0.0&vd_source=b5ef87e2f0a873011363576f8bdcba61
动脑学院 你能听懂的Kotlin协程课,跟老司机学,不用自己瞎折腾
###1.认识协程
协程是什么?协程基于线程,它是轻量级线程
异步任务和协程对比? 协程让异步逻辑同步化,杜绝回调地狱,协程的核心是函数或者一段程序能够被挂起,稍后再在挂起的位置恢复
协程的依赖: kotlinx-coroutines-core, kotlinx-coroutines-android
协程的挂起与恢复: suspend: 暂停执行当前协程,并保存所有的局部变量; resume:让已暂停的协程从其暂停处继续执行
挂起函数:suspend关键字修饰,只能在协程体内或其他挂起函数内调用
挂起和阻塞的区别? 主线程遇到挂起(如协程里调用了delay),先记下挂起点,然后该干嘛干嘛,如刷新UI,等挂起结束了,继续执行下面;
主线程如果阻塞了(如Thread.sleep),就啥事不干,一直等结束(阻塞就是线程被占用了,不能干其他事情)
Choreographer: Skipped xxx frames! The application may be doing too much work on its main thead.
协程两部分:基础设施层和业务框架层
基础设施层代码实例: val continuation=suspend {5}.createCoroutine(object : Continuation<Int> {override fun resumeWith(result: Result<Int>) {}}); continuation.resume(Unit)
协程调度器:
Dispatchers.Main : Android上的主线程
Dispatcher.IO: 非主线程
Dispatcher.Default 非主线程,专为CPU密集型任务进行了优化
协程作用域:
CoroutineScope: 会跟踪所有协程,同样可以取消它所启动的所有协程
GlobalScope: 生命周期是process级别的,即使Activity或Fragment已经被销毁,协程仍然在执行
MainScope:在Activity中使用,可以在onDestory中取消协程,cancel()方法,取消会报异常JobCancellationException: Job was cancelled;
viewModelScope: 只能在ViewModel中使用,绑定ViewModel的生命周期
lifecycleScope:只能在Activity或Fragment中使用,会绑定Activity和Fragment的生命周期
###2.协程的启动与取消
协程构建器:
launch: 返回一个Job并且不附带任何结果
async:返回一个Deferred,Deferred也是一个Job,可以使用await在一个延期的值上得到最终的结果
等待上一个任务执行完:launch+join(); async+await()
runBlocking {} 让我们的主线程变成一个协程
协程的启动模式:
CoroutineStart.DEFAULT: 协程创建后,立即开始调度,在调度前协程如果被取消,其将直接进入取消响应的状态
CoroutineStart.ATOMIC: 协程创建后,立即开始调度,协程执行到第一个挂起点之前不响应取消
CoroutineStart.LAZY: 只有协程被需要时,包括主动调用协程的start,join或者await函数时才会开始调度,如果调度前就被取消,那么该协程将直接进入异常结束状态
CoroutineStart.UNDISPATCHED: 协程创建后立即在当前函数调用栈中执行,直到遇到第一个真正挂起的点
如何指定协程上下文是Dispatchers.IO, 但是协程任务的执行仍然还在主线程? 执行协程启动模式为CoroutineStart.UNDISPATCHED
协程作用域构建器:
coroutineScope与runBlocking
runBlocking是常规函数,coroutineScope是挂起函数
Job对象
对于每一个创建的协程(通过launch或者async),会返回一个Job实例,负责管理协程的生命周期
新创建New,活跃Active, 完成中Completing,已完成Completed,取消中Cancelling,已取消Cancelled,可以访问Job的属性:isActive,isCancelled和isCompleted
取消协程:
取消作用域会取消它的子协程;被取消的子协程不会影响其余兄弟协程;协程通过抛出一个特殊的异常CancellationException来处理取消操作
CPU密集型任务取消
isActive检查Job是否处于活跃状态;ensureActive(),如果job处于非活跃状态会立即抛出异常;yield函数如果取消会抛出CancellationException异常,还会尝试出让线程的执行权
use函数:只能被实现了Closeable的对象使用,程序结束的时候会自动调用close方法,适合文件对象
不想协程被取消: withContext(NonCancellable) {}
超时任务:withTimeout(1000) {}; withTimeoutOrNull(1000) {}
###3.协程的异常处理
协程上下文CoroutineContext是一组定义协程行为的元素
Job:控制协程的生命周期
CoroutineDispatcher: 向合适的线程分发任务
CoroutineName: 协程的名称
CoroutineExceptionHandler: 处理未被捕获的异常
组合协程上下文: launch(Dispatchers.Default + CoroutineName("test")) {} --重载了加号运算符
协程上下文的继承:CoroutineScope协程作用域-》launch协程-》async子协程
新创建的协程,它的CoroutineContext会包含一个全新的Job实例,剩下的元素从CoroutineContext的父类继承,该父类可能是另外一个协程或者创建该协程的CoroutineScope
根协程异常传播:launch里抛出的异常:可以在launch协程里捕捉;async里抛出的异常,调用await后才能捕捉
非根协程的异常:其他协程创建的协程中,产生的异常总是会被传播
异常的传播特性:当一个协程由于异常运行失败时,它会传播这个异常并传递给它的父级,接下来父级会取消它自己的子级,取消它自己,将异常传播并传递给它的父级
SupervisorJob: 一个子协程的运行失败不会影响到其他子协程,SupervisorJob不会传播异常给它的父级,它会让子协程自己处理异常
CoroutineScope(SupervisorJob())
作用域构建器supervisorScope {}, 在supervisorScope里创建的子协程里抛出异常,不会影响其他子协程;如果在supervisorScope作用域里抛出异常,所有子作业将会被全部取消
异常的捕获:使用CoroutineExceptionHandler对协程的异常进行捕获
取消与异常:取消(如joinAndCancel)会抛出CancellationException异常,当子协程被取消时,不会取消它的父协程
异常聚合:CoroutineExceptionHandler { _, exception-> ${exception.suppressed.contentToString() --打印第二个异常}}
###4.Flow-异步流
异步返回多个值: flow<Int> { },
flow: Flow构建器函数; flow{...} 构建快中代码可以挂起; 流使用emit函数发送值; 使用collect函数收集值
Flow应用: Background Thread emit数据 -》Main Thread 去collect数据
冷流: flow构建器中的代码直到流被收集的时候才运行
流构建器:
flowOf()发射固定值的流
使用asFlow扩展函数,可以将各种集合与序列转换成流: (1..3).asFlow().collect { value -> println(value) }
流的上下文:flowOn操作符:用于更改流发射的上下文; launchIn指定在那个协程里收集数据
流的取消: withTimeoutOrNull(2500) {}
流的取消检测: 流构建器对每个发射值执行附加的ensureActive检测以进行取消
如果想流可以被取消,需要明确指定cancellable: (1..5).asFlow().cancellable().collect { value -> if (value == 3) cancel() }
背压: 生产者生产效率大于消费者消费效率
buffer() 并发运行流中发射元素的代码
conflate() 合并发射项; collectLatest()
操作符: 转换操作符: map, transform; 限长操作符:take;
末端操作符: reduce ; 1到5平方并累加 (1..5).asFlow().map{ it*it }.reduce{ a,b->a+b }
组合多个流: zip
展平流: flatMapConcat连接模式, flatMapMerge合并模式, flatMapLatest最新展平模式
流的异常: 收集的时候处理异常: 如check(value <= 1) {} ; 发射的时候抛出异常,使用catch函数捕捉
流的完成: onCompletion