本文链接:https://blog.csdn.net/feather_wch/article/details/132200864
总结:启动、流程、现成选择、启动模式、并发
、原理、结构化并发、调度中心协程作用域、上下文、异常、Flow(背压)、热流
1、线程和协程的区别关系
2、kotlin在不同语言中的区别
3、协程真正的优点:(只是高效,轻量为什么不用Rxjava)?
1、网络请求返回数据,为什么多1个包装类(中间层)?
2、传统异步任务的执行流程
/***
* 委托给CoroutineScope,但是没有实现,交给MainScpoe去实现。就拥有this
*/
class MainActivity6 : AppCompatActivity() , CoroutineScope by MainScope()
launch(){} // 直接调用
override fun onDestroy() {
super.onDestroy()
cancel() // 手动取消
}
// 挂起函数,retrofit高级版本自动加判断会切线程。2.7+
suspend fun loginActionCoroutine()
1、kotlin协程的特点
2、Retrofit关于协程的特殊处理
协程:可以做到一行代码,两次线程切换。代码块线程到异步线程,异步线程到代码块线程。
挂起:暂停,挂起当前线程。保存当前协程局部信息。用于恢复
恢复:从协程挂起点恢复。
阻塞和挂起的区别
阻塞:类似安检的时候睡觉
挂起:类似安检的时候站到一边去
官方框架层 写需求:
GlobalScope.launch{
}
语言基础层 写需求:
// 1、协程体
val suspendFun: suspend ()-> Float = suspend{
delay(10000)
123.456f
}
// 2、协程体执行结果交给Continuation
// 最底层还是回调
val continuation = suspendFun.createCoroutine(object: Continuation<Float>{
override val context: CoroutineContext
get() = Dispatchers.Main // 恢复后的线程
override fun resumeWith(result: Result<Float>) {
println(result) // 回调后执行 //操作result,可以打印
}
})
// 3、协程体的管理者,激活,协程体的执行
continuation.resume(Unit)
// 底层
// continuation.resumeWith(Result.success(value))
2、回调的Continuation和返回的Continuation的区别
3、回调的Continuation内部实现
银行APP痛点,为了安全,一个账号登录需要顺序查询五六个接口。
Retrofit可以线程切换,就是发现suspend关键字后,会调用
withContext(Dispatchers.IO) {
}
进行线程切换
如果有多个请求的结果需要合并怎么办?
- Select选择器
- async()+await()
VMOptions:
-Dkotlinx.coroutines.debug=on
println("${Thread.currentThread().name}")
main的第一行代码 main @coroutine#1
launch main @coroutine#2
async main @coroutine#3
launch2 main @coroutine#2
async2 main @coroutine#3
获取到网络数据JSON
main的第二行代码 main @coroutine#1
1、协程和协程框架的区别
2、kotlinx.coroutines.android支持Android相关的操作
3、协程,在JVM层面,是线程的封装框架。
4、协程没有Dispatchers的时候,默认使用当前线程。
5、使用EmptyCoroutineContext,使用当前的线程的调度器context: CoroutineContext = EmptyCoroutineContext
// Job
var job = launch {
println("launch")
delay(2000)
}
job.join() // 会等待launch执行完成
public interface Job : CoroutineContext.Element
public interface Element : CoroutineContext
获取到返回值
// Deferred
var deferred = async {
println("async")
delay(5000)
"获取到网络数据JSON"
}
println(deferred.await())
3、launch和async都会立即调度
public interface Deferred<out T> : Job
2、执行顺序
fun main() = runBlocking <Unit> {
println("main 111") // 1 【执行顺序1】
// GlobalScope.launch默认是Dispachers.DEFAULT 另一个线程
val job = GlobalScope.launch { // 协程体被创建【执行顺序2】
println("launch 1 start") // 3 【执行顺序5】
delay(1000L) // 【执行顺序6】
println("launch 1 end")
}
println("main 222") // 2 【执行顺序3】
Thread.sleep(500L) // 阻塞当前runBlocking作用域所属的线程 【执行顺序4】
}
输出:
main 111
main 222
launch 1 start
var job1 = launch {
requestUserName()
}
job1.join() // 挂起当前协程体,非阻塞
var job2 = launch {
requestUserInfo()
}
job2.join()
println("查询结束")
====> 线程的join
1、线程的join会阻塞当前线程
2、join会不会释放锁?
synchronized(obj){
thread.join(); //join不释放锁
}
synchronized(thread){
thread.join(); //join释放锁
}
3、yield()让出cpu执行权,不会释放锁
4、park()会释放锁,并等待唤醒
5、sleep()阻塞,不会释放锁
任务按顺序执行:
协程体1
协程体1.join()
协程体2
协程体2.join()
协程体3
协程体3.join()
任务并发执行:
协程体1
协程体2
协程体3
协程体1.join()
协程体2.join()
协程体3.join()
挂起等待协程体的返回值:
var deferred = async {
println("async")
requestUserName()
}
println(deferred.await())
var deferred1 = async {
println("async")
requestUserName()
}
var deferred2 = async {
println("async")
requestUserName()
}
val time = measureTimeMillis {
deferred1.await()
deferred2.await()
}
println(time)
public inline fun measureTimeMillis(block: () -> Unit): Long {
val start = System.currentTimeMillis()
block() // 执行前后计算时间点
return System.currentTimeMillis() - start
}
1、yield让出执行权
// 子协程
val result = supervisorScope {
launch {
println("launch1 start ...")
delay(1000)
throw KotlinNullPointerException("协程1 一秒后 抛出异常")
}
println("supervisorScope run ")
yield()
delay(500) // or delay(1500)
println("supervisorScope run end")
}
// 子协程
val result = supervisorScope {
launch {
println("执行顺序2")
delay(1000)
println("执行顺序3")
}
println("执行顺序1")
yield()
delay(1000)
println("执行顺序4")
}
GlobalScope.launch()采用默认default异步线程。
Dispatcher.Main:是安卓调度器 UI交互的轻量级任务,更新livedata
Default:适合cpu密集型(计算等)核心线程数+1(防止页缺失)数组排序,json大量解析等等
IO:为磁盘和网络IO处理。核心线程数x2
1、调度前
1、协程有哪些启动模式?
2、默认有启动模式:DEFAULT
launch(/*start = CoroutineStart.DEFAULT*/) { // 默认参数
delay(200)
}
// 调度时
DEFAULT -> block.startCoroutineCancellable(completion)
3、执行顺序(1)
println("main start thread:${Thread.currentThread().name}")
// TODO 画图 让大家理解 调度中心 的 图概念 脑海里面有自己图
launch /*(start = CoroutineStart.DEFAULT)*/ { // 子协程
println("launch start thread:${Thread.currentThread().name}")
withContext(Dispatchers.Default) {
println("withContext start thread:${Thread.currentThread().name}")
delay(10000)
}
println("launch end thread:${Thread.currentThread().name}")
}
main start thread:main
launch start thread:main
withContext start thread:DefaultDispatcher-worker-1
======等待10000ms=========
launch end thread:main
下面关于原理的讨论源于:context: CoroutineContext = EmptyCoroutineContext
1、执行顺序(2)
// TODO Default启动模式:协程体被创建后,协程立即开始调度,在调度前 协程被取消,协程会进入"协程取消响应状态" 然后才会取消协程
val job = launch (start = CoroutineStart.DEFAULT) {
// 再调度后(协程执行阶段)
println("launch 再调度后(协程执行阶段)>>>1") // 后输出
println("launch 再调度后(协程执行阶段)>>>2")
println("launch 再调度后(协程执行阶段)>>>3")
println("launch 再调度后(协程执行阶段)>>>4")
println("launch 再调度后(协程执行阶段)>>>5")
delay(1000 * 10)
println("launch 再调度后(协程执行阶段)全部结束<<<<")
}
// 调度前(先有调度的准备阶段)
println("协程立即开始调度中.") // 先输出
println("协程立即开始调度中..")
println("协程立即开始调度中...")
println("协程立即开始调度中....")
println("协程立即开始调度中.....")
// println("协程立即开始调度中...... 取消协程:${job.cancel()}")
delay(0) // 调度前结束,1调度后结束
println("协程立即开始调度中...... 取消协程:${job.cancel()}")
1、执行顺序
// TODO ATOMIC启动模式:协程体被创建后,协程立即开始调度,在调度前 协程被取消, 协程体里面会不响应取消的状态(不理你) 直到第一个挂起点(才理你)才能取消
val job = launch (start = CoroutineStart.ATOMIC) {
// 再调度后(协程执行阶段)
println("launch 再调度后(协程执行阶段)>>>1") // 后输出
println("launch 再调度后(协程执行阶段)>>>2")
println("launch 再调度后(协程执行阶段)>>>3")
println("launch 再调度后(协程执行阶段)>>>4")
println("launch 再调度后(协程执行阶段)>>>5")
println("launch 再调度后(协程执行阶段)>>>6")
println("launch 再调度后(协程执行阶段)>>>7")
println("launch 再调度后(协程执行阶段)>>>8")
println("launch 再调度后(协程执行阶段)>>>9")
println("网络请求 必须做的工作,哪怕是被取消cancel,也要做的工作,请求的网络埋点,笔记埋点请求的信息 上报给服务器 ...")
// ... 不管你 cancel 不 cancel,这个工作一定会执行
// 打个比方:下面代码是 网络请求 耗时
delay(1000 * 10) // 第一个挂起点,才是取消状态响应中,然后取消,所以下面不会输出
println("launch 再调度后(协程执行阶段)全部结束<<<<")
}
// 调度前(先有调度的准备阶段)
println("协程立即开始调度中.") // 先输出
println("协程立即开始调度中..")
println("协程立即开始调度中...")
println("协程立即开始调度中....")
println("协程立即开始调度中.....")
println("协程立即开始调度中...... 取消协程:${job.cancel()}")
// TODO LAZY启动模式:协程体被创建后,不会调度,会一直等 我们来手动调度(start非挂起,join挂起,await挂起),才开始调度,
// 在调度前 协程被取消,协程会进入"协程取消响应状态" 然后才会取消协程 == Default模式
val job = launch (start = CoroutineStart.LAZY) {
// 再调度后(协程执行阶段)
println("launch 再调度后(协程执行阶段)>>>1") // 后输出
println("launch 再调度后(协程执行阶段)>>>2")
println("launch 再调度后(协程执行阶段)>>>3")
println("launch 再调度后(协程执行阶段)>>>4")
println("launch 再调度后(协程执行阶段)>>>5")
println("launch 再调度后(协程执行阶段)>>>6")
println("launch 再调度后(协程执行阶段)>>>7")
println("launch 再调度后(协程执行阶段)>>>8")
println("launch 再调度后(协程执行阶段)>>>9")
delay(1000 * 10)
println("launch 再调度后(协程执行阶段)全部结束<<<<")
}
job.start() // 最常用的 (非挂起) 【手动调度】
// job.join() // 【手动调度】
// async{}.await // 【手动调度】
// 调度前(先有调度的准备阶段)
println("协程立即开始调度中.") // 先输出
println("协程立即开始调度中..")
println("协程立即开始调度中...")
println("协程立即开始调度中....")
println("协程立即开始调度中.....")
delay(0)
println("协程立即开始调度中...... 取消协程:${job.cancel()}")
1、执行流程
// TODO UNDISPATCHED启动模式:协程体被创建后,(立即 在当前调用栈线程中 执行),没有调度, 协程体里面{一直执行到 第一个挂起点, 然后再执行 父协程的代码}
// 此模式,协程体被创建后,立即执行,没有调度,既然这么快,请问这个协程是依附在哪个线程呢?
val job = launch (context = Dispatchers.IO, start = CoroutineStart.UNDISPATCHED) {
println("launch UNDISPATCHED 立即执行,没有调度 (协程执行阶段)>>>1") // 后输出
println("launch UNDISPATCHED 立即执行,没有调度(协程执行阶段)>>>2 thread:${Thread.currentThread().name}")
println("launch UNDISPATCHED 立即执行,没有调度(协程执行阶段)>>>3")
println("launch UNDISPATCHED 立即执行,没有调度(协程执行阶段)>>>4")
println("launch UNDISPATCHED 立即执行,没有调度(协程执行阶段)>>>5")
println("launch UNDISPATCHED 立即执行,没有调度(协程执行阶段)>>>6")
println("launch UNDISPATCHED 立即执行,没有调度(协程执行阶段)>>>7")
println("launch UNDISPATCHED 立即执行,没有调度(协程执行阶段)>>>8")
println("launch UNDISPATCHED 立即执行,没有调度(协程执行阶段)>>>9")
// 这个就是第一个挂起点
delay(1000 * 10)
println("launch 再调度后(协程执行阶段)全部结束<<<<")
}
// UNDISPATCHED 没有调度前
println("UNDISPATCHED 没有调度前.") // 先输出
println("UNDISPATCHED 没有调度前..")
println("UNDISPATCHED 没有调度前...")
println("UNDISPATCHED 没有调度前....")
println("UNDISPATCHED 没有调度前.....")
println("UNDISPATCHED 没有调度前...... 取消协程:${job.cancel()}") // 只能取消 delay(1000 * 10)
1、协程会面临的问题:
2、协程任务的不可控 出现了【结构化并发】-CoroutineScope
3、所有协程体都有CoroutineScope有什么用?
4、launch最后一次参数给CoroutineScope增加了匿名扩展函数
5、withContext也有sc才能管理
CoroutineScope常见子类:
CoroutineScope内部转交给CoroutineContext
大写的方法名是什么设计模式?
class MainActivity: CoroutineScope by MainScope(){} // =====> 类委托
内部拥有this:可以省略
一个类将接口的实现委托给另一个对象
MainActivity将接口CoroutineScope的实现委托给MainScope()
public enum class CoroutineStart {
DEFAULT,
LAZY,
ATOMIC,
UNDISPATCHED;
public operator fun <T> invoke(block: suspend () -> T, completion: Continuation<T>): Unit =
when (this) {
DEFAULT -> block.startCoroutineCancellable(completion)
ATOMIC -> block.startCoroutine(completion)
UNDISPATCHED -> block.startCoroutineUndispatched(completion)
LAZY -> Unit // will start lazily
}
public operator fun <R, T> invoke(block: suspend R.() -> T, receiver: R, completion: Continuation<T>): Unit =
when (this) {
DEFAULT -> block.startCoroutineCancellable(receiver, completion)
ATOMIC -> block.startCoroutine(receiver, completion)
UNDISPATCHED -> block.startCoroutineUndispatched(receiver, completion)
LAZY -> Unit // will start lazily
}
public val isLazy: Boolean get() = this === LAZY
}
1、Job六大生命周期
New: 协程体被创建
Active: 存活状态、活跃中
Canceling: 取消响应状态 => job.cancel()
->Cancelled: 已取消
Completing: 完成中
Completed: 已完成
2、查看job状态
val job = launch {
delay(1000)
}
// true false false // Active
println("${job.isActive} ${job.isCancelled} ${job.isCompleted}")
delay(1)
job.cancel()
// false true false // Cancelled
println("${job.isActive} ${job.isCancelled} ${job.isCompleted}")
delay(1)
// false true true // Completed
println("${job.isActive} ${job.isCancelled} ${job.isCompleted}")
3、job生命周期有什么用呢?
4、传入一个已经取消的Job到协程中,会导致协程不再执行
执行完成时回调,比在join下面打印信息更专业
val job = launch {
delay(1000)
println("Hello")
}
job.invokeOnCompletion {
println("job 执行 完成")
}
1、父协程Job的子协程Job列表
job.children
2、父协程会等待所有子协程执行完成
//取消和挂起join合并
var item = 0
// Default == 每循环一次计算一次 = CPU密集型运算 = 很密集的运算工作
val job1 = launch (context = Dispatchers.Default) {
val start = System.currentTimeMillis()
while (item <= Int.MAX_VALUE && isActive) {
if (System.currentTimeMillis() > start) {
println("while item:${item++}")
}
}
}
// 调度前 - 协程体 准备工作期
// job1.cancel()
// 调度后 - 协程体 执行期
delay(50)
job1.cancel()
fun main() = runBlocking <Unit> {
var item = 0
// Default == 每循环一次计算一次 = CPU密集型运算 = 很密集的运算工作
val job1 = launch (context = Dispatchers.Default) {
val start = System.currentTimeMillis()
while (item <= Int.MAX_VALUE) {
ensureActive()
if (System.currentTimeMillis() > start) {
println("while item:${item++}")
}
}
}
// 调度前 - 协程体 准备工作期
// job1.cancel()
// 调度后 - 协程体 执行期
delay(50)
/*job1.cancel()
job1.join()*/
job1.cancelAndJoin()
println("最后的值 最后打印是 多少呢:$item")
}
public fun Job.ensureActive(): Unit {
if (!isActive) throw getCancellationException()
}
1、yield =================> Java yield
CancellactionException
fun main() = runBlocking <Unit> {
var item = 0
// Default == 每循环一次计算一次 = CPU密集型运算 = 很密集的运算工作
val job1 = launch (context = Dispatchers.Default) {
val start = System.currentTimeMillis()
while (item <= Int.MAX_VALUE) {
yield() / 让出资源
if (System.currentTimeMillis() > start) {
println("while item:${item++}")
}
}
}
delay(50)
// job1.cancelAndJoin()
job1.cancel()
println("最后的值 最后打印是 多少呢:$item")
}
1、文件、数据库等操作的时候,直接cancel会导致,资源没办法正确关闭
try-catch-finally中捕获
作用:出现异常时,关闭资源
val job = launch {
BufferedReader(FileReader("D:\\Derry.txt"))
.use {
var lineContent: String?
while (true) {
delay(1000)
lineContent = it.readLine()
lineContent ?: break // 如果读取的内容是空的,就跳出循环循环
println(lineContent)
}
}
}
// 调度后
delay(2000)
job.cancel()
源码分析:
public inline fun <T : Closeable?, R> T.use(block: (T) -> R): R {
// 省略
try {
return block(this)
} catch (e: Throwable) {
throw e
} finally {
// 省略
close()
}
}
1、finally中的代码一定执行吗?
// 需要有delay()等挂起操作
finally {
for (i in 1..Int.MAX_VALUE) {
println("finally i:$i")
delay(1000L)
}
}
// finnally中
try {
//
} finally {
withContext(NonCancellable) {
for (i in 1..Int.MAX_VALUE) {
println("finally i:$i")
delay(1000L)
}
}
}
// 其他地方
val job = launch {
withContext(NonCancellable) {
for (i in 1..Int.MAX_VALUE) {
println("item i:$i")
delay(1000L)
}
}
}
withTimeout(1000){
// 超时抛出异常
}
val r = withTimeoutOrNull(6000) {
for (i in 1 .. 6) {
println("for i:$i")
delay(500)
}
"执行完成"
}
println("成果是: ${r ?: "执行出现问题"}")
结构化并发的实战:作用域构建器
1、runBlocking
// 线程变协程
fun main() = runBlocking<Unit> {
// this持有了CoroutineScope
// 就可以执行launch和aysnc
}
@Throws(InterruptedException::class)
public fun <T> runBlocking(context: CoroutineContext = EmptyCoroutineContext, block: suspend CoroutineScope.() -> T): T {
val currentThread = Thread.currentThread()
val contextInterceptor = context[ContinuationInterceptor]
val eventLoop: EventLoop?
val newContext: CoroutineContext
if (contextInterceptor == null) {
eventLoop = ThreadLocalEventLoop.eventLoop
newContext = GlobalScope.newCoroutineContext(context + eventLoop)
} else {
eventLoop = (contextInterceptor as? EventLoop)?.takeIf { it.shouldBeProcessedFromContext() }?: ThreadLocalEventLoop.currentOrNull()
newContext = GlobalScope.newCoroutineContext(context)
}
val coroutine = BlockingCoroutine<T>(newContext, currentThread, eventLoop)
coroutine.start(CoroutineStart.DEFAULT, coroutine, block)
return coroutine.joinBlocking()
}
2、runBlocking是阻塞的,协程是非阻塞的不冲突吗?
3、结构化并发好处=>解决协程不可控
1、coroutineScope和runBlocking区别
fun main() = runBlocking<Unit> {
coroutineScope {
}
}
fun main() = runBlocking<Unit> {
coroutineScope {
launch {}
launch {}
launch {
throw KotlinNullPointerException()
}
// 一个失败,全部失败
}
}
1、supervisorScope是什么?
2、返回值
val result = supervisorScope {
launch {
println("launch1 start ...")
delay(1000)
throw KotlinNullPointerException("协程1 一秒后 抛出异常")
}
launch {
println("launch5 start ...")
delay(2300)
println("launch5 end ...")
}
'男'
}
3、作用域出现异常,所有子协程都不会执行
// 子协程
val result = supervisorScope {
launch {}
launch {}
launch {}
launch {}
// 你敢在 supervisorScope {} 里面抛出异常,这个作用域里面所有的协程都会被取消
throw KotlinNullPointerException("supervisorScope {} 里面抛出异常")
'男'
}
1、CoroutineScope()和coroutineScope()区别
2、CoroutineScope构造作用域
fun main() = runBlocking<Unit> {
val scope = CoroutineScope(context = Dispatchers.Default)
scope.launch {
println("scope.launch")
delay(1000)
println("scope.launch2") // 这一行不会打印
}
}
3、CoroutineScope的作用是什么?为什么所有协程体,必须有CoroutineScope?
val derryScope = CoroutineScope(context = mDerryThread)
derryScope.launch {
println("launch 1 start")
delay(1000L)
println("launch 1 end")
}
derryScope.launch {
println("launch 2 start")
delay(1000L)
println("launch 2 end")
}
derryScope.cancel() //取消
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iWGKXbji-1692500679703)(…/…/images/646516685963cd35dcc862e0fc77cd34f3d4a6533bdaa43232ee3811fcf59472.png)]
3、CoroutineScope构造作用域和cancel的执行顺序:cancel统一管理,统一退出
fun main() = runBlocking<Unit> {
val scope = CoroutineScope(context = Dispatchers.Default)
// 调度前
scope.launch {
println("scope.launch") // 调度后, 一定会执行
delay(1000)
println("scope.launch2") // 这一行不会打印
}
scope.cancel() // 调度后执行cancel
}
// 调度前
val loginJob = scope.launch {
// 登录请求
}
loginJob.cancel()
1、SequenceScope是一个接口,它定义了创建序列的生成器函数(sequence builder function)的作用域。
2、SequcenceScope包含以下方法:用于添加到生成的序列中
yield(value: T)
yieldAll(elements: Sequence)
yieldAll(elements: Iterable)
:将一个可迭代对象中的所有元素添加到生成的序列中。yieldAll(elements: Iterator)
:将一个迭代器中的所有元素添加到生成的序列中。yieldAll(elements: Array)
:将一个数组中的所有元素添加到生成的序列中。1、Sequence是什么?
2、Sequence应用场景
3、过滤和映射数据:
val numbers = sequenceOf(1, 2, 3, 4, 5)
val filteredAndMapped = numbers
.filter { it % 2 == 0 } // 过滤出偶数
.map { it * it } // 将每个数平方
for (result in filteredAndMapped) {
println(result) // 输出结果为:4 16
}
4、惰性计算:
val infiniteSequence = generateSequence(0) { it + 1 } // 生成一个无限递增的序列 generateSequence是官方API 参数0是种子
val firstTen = infiniteSequence
.take(10) // 只取序列的前十个元素
for (number in firstTen) {
println(number) // 输出结果为:0 1 2 3 4 5 6 7 8 9
}
4、链式操作:
val numbers = sequenceOf(1, 2, 3, 4, 5)
val result = numbers
.filter { it % 2 == 0 } // 过滤出偶数
.map { it * it } // 将每个数平方
.reduce { acc, value -> acc + value } // 求和
println(result) // 输出结果为:20
1、SequenceScope
3、sequence是什么?作用是什么?
yield
和yieldAll
方法逐步生成序列的元素。fun fibonacciSequence(): Sequence<Int> = sequence {
var current = 0
var next = 1
while (true) {
yield(current)
val sum = current + next
current = next
next = sum
}
}
fun main() {
val fibonacci = fibonacciSequence()
val firstTen = fibonacci.take(10).toList()
println(firstTen) // 输出:[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
}
4、不允许自己调用挂起函数,只允许yield等内置方法具有挂起能力
fun getSequcence() = sequence{
for(item in 100..110){
// 不允许使用 delay
yield(item)
}
}
// 源码:
@RestrictsSuspension
@SinceKotlin("1.3")
public abstract class SequenceScope<in T> internal constructor() {
public abstract suspend fun yield(value: T)
// ...
}
CoroutineContext的内部元素
Job: 控制协程的生命周期 协程协程行为的元素
CoroutineDispatcher:向合适的线程分发协程任务
CoroutineName:协程的名称,测试调试的时候,非常有用
CoroutineExceptionHandler:处理未被捕捉的异常
val job = launch(context = Dispatchers.Default + CoroutineName("我是Derry协程") + CoroutineExceptionHandler{
coroutineContext, throwable ->
println("CoroutineContext:$coroutineContext Throwable:$throwable")
} + Job()) {
println("launch this thread:${Thread.currentThread().name}")
}
// CoroutineContext = CoroutineDispatcher+CoroutineName+CoroutineExceptionHandler+Job
1、协程CoroutineContext的继承
2、CoroutineContext的子类有哪些
3、下面代码中子协程所在的线程池是什么?
val coroutineScope = CoroutineScope(Job() + exception + Dispatchers.Main + CoroutineName("coroutineD"))
coroutineScope.launch(Dispatchers.Default) {
launch {
println("launch1 从上下文获取协程:${coroutineContext[Job]} --- 当前线程与协程:${Thread.currentThread().name}")
}
launch {
println("launch2 从上下文获取协程:${coroutineContext[Job]} --- 当前线程与协程:${Thread.currentThread().name}")
}
}
协程Default
coroutineScope.launch(Dispatchers.IO) {xxx}
协程Default:为什么改成IO还是Default?因为Default线程池很大,IO会复用Default可用的线程池。
作用是什么?
默认是 EmptyCoroutineContext
1、coroutineContext的作用
2、通过coroutineContext拿到上下文
// 1、suspend编译之后 剔除suspend 用 Continuation来代替
// 2、Continuation包含了coroutineContext,所以可以拿到上下文
suspend fun requestAction() : CoroutineContext = coroutineContext
fun main() {
GlobalScope.launch {
println(requestAction()) // [StandaloneCoroutine{Active}@21ccc917, Dispatchers.Default]
}
Thread.sleep(1000L)
}
1、Continuation包含了coroutineContext
1、ExecutorService
2、ExecutorService接口的常见实现类
1、自定义线程池,将线程池转为调度器
val mDerryThread = Executors.newSingleThreadExecutor {
Thread(it, "DerryCustomThread") // 自定义
.apply { isDaemon = true } // 守护线程
}.asCoroutineDispatcher()
// 构造出ExecutorCoroutineDispatcher
GlobalScope.launch(context = mDerryThread) { //默认DEFAULT线程池,这里设置了新的Dispatchers
println("父协程 ${Thread.currentThread().name}")
launch {
println("子协程 1 ${Thread.currentThread().name}")
}
launch {
println("子协程 2 ${Thread.currentThread().name}")
}
}
// FixedThreadPool
val mDerryThread = Executors.newFixedThreadPool(32).asCoroutineDispatcher()
1、launch和async异常捕获
launch {
try {
throw KotlinNullPointerException("is null")
} catch (e:Exception) {
println("launch catch exception:$e")
}
}
async {
try {
throw KotlinNullPointerException("is null")
} catch (e:Exception) {
println("async catch exception:$e")
}
}
val d = async {
throw KotlinNullPointerException("is null")
}
// 协程自动传播异常 TODO >>>>>>>>>>>>>>>>>>>>>>>
捕获到了异常,但还是会直接退出,抛出异常
async 返回值 catch exception:kotlin.KotlinNullPointerException: is null // 捕获到了
Exception in thread "main" kotlin.KotlinNullPointerException: is null
为什么会出错?
1、异常返回可以捕获
// 全局作用域就可以用了,不是子协程
val d = GlobalScope.async {
throw KotlinNullPointerException("is null")
}
// 协程自动传播异常 TODO >>>>>>>>>>>>>>>>>>>>>>>
try {
d.await()
}catch (e : Exception) {
println("async 返回值 catch exception:$e")
}
TIPS:最顶层记得用async
在拥有ExceptionHandler的前提下
val exception = CoroutineExceptionHandler { _, e ->
println("你的协程发生了异常 e:$e")
}
1、launch的异常,调用join不会导致异退
val job1 = GlobalScope.launch(exception) {
launch {
launch {
launch {
launch {
launch {
launch {
throw KotlinNullPointerException("launch 我错了")
}
}
}
}
}
}
}
// 注意这里JOIN
job1.join()
// 还是
try {
job1.join()
}catch (e: Exception) {
println(e)
}
2、外部是launch,内部async同理
val job2 = GlobalScope.launch(exception) {
async {
async {
async {
async {
async {
async { // async协程体被创建后,立即开始调度 执行
throw KotlinNullPointerException("async1 我错了")
}
}
}
}
}
}
}
job2.join()
3、最外层async,调用await不捕获会导致异退
val d = GlobalScope.async (exception) {
async {
async {
async {
async {
async {
async { // async协程体被创建后,立即开始调度 执行
throw KotlinNullPointerException("async2 我错了")
}
}
}
}
}
}
}
// async 的 await 才能 得到返回值 与 异常处理
d.await()
4、为什么会有这个差别?
Deferred>>
5、如果没有ExceptionHandler,launch的join是否捕获都没用,都会崩溃
val job = GlobalScope.async {
launch {
launch {
launch {
throw KotlinNullPointerException("我错了")
}
}
}
}
// 开始捕获顶级协程的 GlobalScope.async
try {
job.await()
} catch (e: Exception) {
println("e:$e")
}
val scope = CoroutineScope(SupervisorJob())
val j1 = scope.launch {
println("launch1 start ...")
delay(1000)
throw KotlinNullPointerException("is null")
}
val j2 = scope.launch {
println("launch2 start ...")
delay(1300)
println("launch2 end ...")
}
// joinAll(j1, j2) // 协程1抛出异常、协程2执行完成
// 2秒后的协程必须全部取消
delay(2000)
scope.cancel()
// 灵活搭配
1、应用场景
1、顶级协程设置CoroutineExceptionHandler,才有能力处理好异常的捕获
// 正常捕获异常版本
val job1 = GlobalScope.launch(exception) {
launch {
launch {
launch {
launch {
launch {
launch {
throw KotlinNullPointerException("launch 我错了")
}
}
}
}
}
}
}
job1.join()
// 一定会闪退版本
// 需要调用join(),不调用不会闪退
val job1 = GlobalScope.launch{
launch {
launch {
launch {
launch(exception) {
launch {
launch {
throw KotlinNullPointerException("launch 我错了")
}
}
}
}
}
}
}
job1.join()
1、全局异常捕获 =====> APM 协程 Crash
resources
目录META-INT
,再创建目录services
kotlinx.coroutines.CoroutineExceptionHandler
com.personal.tax.study.AllCoroutineExceptionHandler
(包名+类名)class AllCoroutineExceptionHandler : CoroutineExceptionHandler{
override val key: CoroutineContext.Key<*>
get() = CoroutineExceptionHandler
override fun handleException(context: CoroutineContext, exception: Throwable) {
// 上报异常给服务器
Log.d("Derry", "全局异常捕获 handleException e:$exception")
}
}
2、全局异常捕获有什么用?
1、取消异常不捕获会静默处理
fun main() = runBlocking<Unit> {
val loginJob = GlobalScope.launch {
try {
delay(2000)// 捕获异常 CancellationException
} catch (e: CancellationException) {
println("协程被取消了 异常是:$e")
}
}
loginJob.cancel() // 会抛出异常
// 不捕获异常,会静默处理
loginJob.join() // 等待结果
}
2、取消异常有什么用?后续要暴露问题,进行处理。
3、子协程非JobCancellationException会影响兄弟协程吗?
1、如何处理多个异常
val exception = CoroutineExceptionHandler { _, e ->
println("你错了:$e 多个异常:${e.suppressed.contentToString()}")
}
// 顶级协程
val job = GlobalScope.launch(exception) {
launch {
delay(3000)
throw KotlinNullPointerException("launch1 异常")
}
launch {
try {
delay(20000000)
} finally {
throw KotlinNullPointerException("launch2 异常")
}
}
// 不属于 子协程
// 父子协程的 结构化并发被你打乱了
launch(Job()) {
try {
delay(20000000)
} finally {
throw KotlinNullPointerException("launch3 异常")
}
}
launch {
try {
delay(20000000)
} finally {
throw KotlinNullPointerException("launch4 异常")
}
}
launch {
try {
delay(20000000)
} finally {
throw KotlinNullPointerException("launch5 异常")
}
}
launch {
try {
delay(20000000)
} finally {
throw KotlinNullPointerException("launch6 异常")
}
}
}
job.join()
1、Flow是什么?
2、传统事件处理方案:同步、sequence、异步delay
// 1、同步:
fun getList() = listOf(100, 200, 300, 400, 500, 600)
val job = GlobalScope.launch {
getList().forEach{println(it)}
}
job.join()
// 2、异步: 在SequenceScope中,禁止自己调用挂起,除了库内部的函数yield可以挂起
fun getSequcence() = sequence{
for(item in 0..1000){
// 不允许使用 delay
yield(item) // 可以做到协程间切换
}
}
val job2 = GlobalScope.launch {
getSequcence().forEach {
println(it)
}
}
job2.join()
// 3、异步: 挂起,但没有协作
suspend fun getSuspendSequcence(): List<Int> {
delay(1000)
return listOf(100, 200, 300, 400, 500, 600)
}
val job3 = GlobalScope.launch {
getSuspendSequcence().forEach {
println(it)
}
}
job3.join()
1、flow的作用
// 1、类似Observable
suspend fun getFlow() = flow{
for(item in 1..8){
// 发射
emit(item)
}
}
val job = GlobalScope.launch {
// 2、类似RxJava消费,subscribe === Observer消费
getFlow().collect{
println(it)
}
}
job.join()
2、getFlow()可以不用suspend修饰,更自由
fun getFlow() = flow{
for(item in 1..8){
// 发射
emit(item)
}
}
1、Flow可以完全替换LiveData ===> LiveData
// Step 1 : 网络请求
fun fetchData() = flow{
for(item in 1..100){
emit("get Json String = $item")
}
}
// Step 2 : ViewModel抛弃LiveData使用flow
class MyViewModel: ViewModel(){
val dataFlow: Flow<String> = fetchData()
}
// Step 3 : 订阅,并且collect返回数据
class MyFragment: Fragment(){
val viewModel: MyViewModel by viewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
lifecycleScope.launch {
viewModel.dataFlow.collect{
// 在Lifecycle的CoroutineScope中,订阅冷流
println(it)
}
}
}
}
1、Kotlin的flowOn替代了subscribeOn, 对上游进行了切换 ====> RxJava
fun main() = runBlocking<Unit> { // 顶级协程
launch(Dispatchers.Default) {
NetworkRequest.uploadRequestAction()
.flowOn(Dispatchers.IO) // flow运行在IO线程池,默认是main。替换了subscribeOn
// .observeOn(Dispatchers.Default) // 不再需要observeOn,在需要的线程collect即可
.collect {
println("$it%") // 显示下载进度
}
}
// flowOn是给上游还是下游切换?
// 都是给上游
}
object NetworkRequest {
fun uploadRequestAction() = flow {
println("uploadRequestAction thread:${Thread.currentThread().name}")
for (item in 1..100) {
delay(100)
emit(item) // 反馈文件上传进度
}
}
}
1、flow和RxJava都是冷流
1、简化发射源 ===> 高阶函数
toFlow
转换为flow// 1、无参非挂起函数 toFlow
private fun<T> (()->T).toFlow() = flow{
emit(invoke())
}
// 使用:
val r: () -> String = ::getFlowValue
r.toFlow().collect { println(it) }
// 2、String toFlow
//private fun String.toFlow() = flow{
// emit(this@toFlow) // this@toFlow有markdown错误
//}
// 使用:
"String".toFlow().collect { println(it) }
// 3、无参挂起函数
private fun <OUTPUT> (suspend () -> OUTPUT).toFlow() = flow {
emit(invoke())
}
// 使用:
::getFlowValueSuspend.toFlow().collect { println(it) }
// 4、所有集合toFlow
private fun <E> Iterable<E>.toFlow() = flow {
this@toFlow.forEach { emit(it) }
}
// 使用:
listOf(1, 2, 3, 4, 5, 6).toFlow().collect { println(it) }
setOf(100, 200, 300, 400, 500, 600).toFlow().collect { println(it) }
// 5、sequence的toFlow
private fun <T> Sequence<T>.toFlow() = flow {
this@toFlow.forEach { emit(it) }
}
// 使用
sequence {
yield("Derry1")
yield("Derry2")
yield("Derry3")
}.toFlow().collect { println(it) }
// 6、Array系列处理
//private fun Array.toFlow() = flow {
// // [email protected] { emit(it) }
// repeat([email protected]) {
// emit(this@toFlow[it])
// }
//}
//
//private fun IntArray.toFlow() = flow {
// for (i in this@toFlow) {
// emit(i)
// }
//}
//
//private fun LongArray.toFlow() = flow {
// for (i in this@toFlow) {
// emit(i)
// }
//}
// 7、Range
// 注意第4步,就已经覆盖Range的情况
private fun IntRange.toFlow() = flow {
this@toFlow.forEach { emit(it) }
}
private fun LongRange.toFlow() = flow {
this@toFlow.forEach { emit(it) }
}
1、可变参数实现单个数据或者多个数据都可以转为flow
private fun <T> flows(vararg value: T) = flow{
value.forEach {
emit(it)
}
}
使用
flows("Hello").collect{ println(it) }
flows(1,2,3,4,5).collect{ println(it) }
2、使用官方的flowOf
flowOf("Hello").collect{ println(it) }
flowOf(1,2,3,4,5).collect{ println(it) }
1、协程中上游不可以使用withContext,只能使用flowOn
1、launchIn的作用
// 发射源区域
fun getFlowValue() =
listOf(100, 200, 300, 400, 500, 600)
.asFlow()
.onEach { delay(2000) }
.flowOn(Dispatchers.Default)
// 收集消费区域
val job = getFlowValue()
.onEach { println("thread:${Thread.currentThread().name} $it") }
.launchIn(CoroutineScope(Dispatchers.IO + CoroutineName("自定义协程"))) // 打开水龙头
job.join() // 需要等待执行完成,不然外面main执行结束了。
输出结果
thread:DefaultDispatcher-worker-3 @自定义协程#2 100
thread:DefaultDispatcher-worker-1 @自定义协程#2 200
thread:DefaultDispatcher-worker-1 @自定义协程#2 300
thread:DefaultDispatcher-worker-1 @自定义协程#2 400
thread:DefaultDispatcher-worker-1 @自定义协程#2 500
thread:DefaultDispatcher-worker-1 @自定义协程#2 600
1、协程取消,会导致Flow管道流也会取消。每次都delay 1000,可以正确检测异常
fun getFlow() = flow {
(1..10).forEach { emit(it) }
}.onEach { delay(1000) }
getFlow().collect {
println(it)
if (it == 5) cancel()
}
输出结果
1
2
3
4
5
Exception in thread "main" kotlinx.coroutines.JobCancellationException
2、cancellable:取消不及时,速度太快了,增加监测机制
(1..10).asFlow().collect {
println(it)
if (it == 5) cancel()
}// 会输出1~10,才抛出异常
(1..10).asFlow().cancellable().collect {
println(it)
if (it == 5) cancel()
}// 可以正确捕获到
1、背压是什么?
// 数据过多,会导致消费不来
fun getFlow() = flow {
(1..10).forEach {
delay(500L)
emit(it) // 一秒钟发射一个 一秒钟发射一个 ....
println("生成了:$it thread:${Thread.currentThread().name}")
}
}
// 消费慢
val t = measureTimeMillis {
getFlow().collect {
delay(1000L)
println("消费了:$it thread:${Thread.currentThread().name}")
}
}
println("上游 下游 共 消耗:$t 时间")
// 共消耗15495ms
// 都在一个线程处理,按顺序,放一个取一个
2、buffer:设立缓冲区,减少背压的数量【解决办法一】
fun getFlow() = flow {
(1..10).forEach {
delay(500L)
emit(it) // 一秒钟发射一个 一秒钟发射一个 ....
println("生成了:$it thread:${Thread.currentThread().name}")
}
}
.buffer(100) // 设置缓冲区,减少 背压
// 共消耗11272ms
3、flowOn(Dispatchers.IO):另一个线程处理【解决办法二】
fun getFlow() = flow {
(1..10).forEach {
delay(500L)
emit(it) // 一秒钟发射一个 一秒钟发射一个 ....
println("生成了:$it thread:${Thread.currentThread().name}")
}
}
.buffer(100) // 设置缓冲区,减少 背压
.flowOn(Dispatchers.IO)
// 共消耗11001ms
1、conflate作用:只消费当前认为最新的值,会丢失部分信息
val t = measureTimeMillis {
getFlow().conflate().collect{
delay(1000L)
println("消费了:$it thread:${Thread.currentThread().name}")
}
}
println("上游 下游 共 消耗:$t 时间")
// 共消耗7303ms
1、collectLatest:只收集最新值,速度大幅度提升
val t = measureTimeMillis {
getFlow().collectLatest {
delay(1000L)
println("消费了:$it thread:${Thread.currentThread().name}")
}
}
println("上游 下游 共 消耗:$t 时间")
// 共消耗6869ms
1、transform将上游数据转换后交给下游 ====> LiveData
listOf(100, 200, 300, 400, 500, 600)
.asFlow()
.transform {
this.emit("你好啊数字$it")
}.collect { println(it) }
1、take限制发送的长度,只要前面几个
listOf(100, 200, 300, 400, 500, 600)
.asFlow()
.take(4)
.collect { println(it) }
2、自定义take
fun <INPUT> Flow<INPUT>.myTake(number:Int):Flow<INPUT>{
require(number > 0){"Request element count 0 show be positive"}
return flow {
var i = 0
collect{// collect收集的n个数据,构造了flow{}
if(i++ < number){
return@collect emit(it)
}
}
}
}
末端操作符:适合累加
val r = (1..100)
.asFlow()
.reduce { p1, p2 ->
val result = p1 + p2
result
}
println(r)
(100..200).toFlow().filter { it % 50 == 0 }.map { "map result:$it" }.collect{ println(it) }
1、zip合并Flow
fun getNames() = listOf("杜子腾", "史珍香", "刘奋").asFlow().onEach { delay(1000) }
fun getAges() = arrayOf(30, 40, 50).asFlow().onEach { delay(2000) }
// 合并 组合 操作符 zip
getNames().zip(getAges()) { p1, p2 ->
"name:$p1, age:$p2"
}.collect {
println(it)
}
输出:
name:杜子腾, age:30
name:史珍香, age:40
name:刘奋, age:50
2、zip合并的两个Flow数据长度不一样会怎么办?
fun getNames() = listOf("杜子腾", "史珍香", "刘奋").asFlow().onEach { delay(1000) }
fun getAges() = arrayOf(30, 40, 50, 60, 70).asFlow().onEach { delay(2000) }
zip之后输出结果:会抛弃不匹配的信息60、70
name:杜子腾, age:30
name:史珍香, age:40
name:刘奋, age:50
转换
1、flatMapxxx作用是展平
Flow>
collect { it.collect { a -> println(a)} }
手动展平// 不展平相当于 Flow嵌套,如:Flow>
// 这里发送两次事件,属于Flow
fun runWork(inputValue:Int) = flow {
emit("$inputValue 号员工开始工作了")
delay(1000L)
emit("$inputValue 号员工结束工作了")
}
(1..6).asFlow()
.onEach { delay(1000L)}
.map { runWork(it) } // Flow> // Flow嵌套
.collect { it.collect { a -> println(a)} }
// 展平 操作符 flatMap
getNumbers()
.onEach { delay(1000L)}
// .flatMap { } // 已经废弃
.flatMapConcat { runWork(it) }
// .flatMapMerge { runWork(it) }
// .flatMapLatest { runWork(it) }
.collect { println(it) }
2、flatMapConcat:拼接,常用
3、flatMapMerge
4、flatMapLatest
1、flow合并,执行,并且获得结果
data class Home(val info1: String, val info2: String)
data class HomeRequestResponseResultData(val code: Int, val msg: String, val home: Home)
// 请求本地加载首页数据
fun CoroutineScope.getHomeLocalData(userName: String) = async (Dispatchers.IO) {
delay(3000)
Home("数据1...", "数据1...")
}
// 请求网络服务器加载首页数据
fun CoroutineScope.getHomeRemoteData(userName: String) = async (Dispatchers.IO) {
delay(6000)
Home("数据3...", "数据4...")
}
// 流程
// 1.把多个函数 拿过来
// 2.组装成协程
// 3.包装成FLow
// 4.Flow合并 得到 结果
coroutineScope {
val r = listOf(::getHomeLocalData, ::getHomeRemoteData) // 1.把多个函数 拿过来
.map {
it("Derry用户") //it.call("Derry用户") 需要引入Kotlin反射 2.组装成协程,调用
}.map {
flow { emit(it.await()) }// 3.包装成FLow
}
val r2 = r.merge() // 4.Flow合并 得到 结果
r2.collect { println(it) }
}
捕获上游的异常
flow {
listOf(100).forEach { value ->
emit(value)
throw KotlinNullPointerException("上游抛出了异常")
}
}
.catch {
println("e:$it")
emit(200)
}
.onEach { delay(1000L) }
.collect { println(it) }
1、Flow正常结束,声明式
getNumbers().onCompletion { println("协程Flow结束了") }.collect{println(it)}
2、onCompletion来捕获异常结束:上游和下游都可以
// 上游
getNumbers2().onCompletion {
if (it != null) { // 非正常结束 是异常结束
println("上游 发生了异常 $it")
}
}
.catch { println("被catch到了 上游 发生了异常 $it") } // .catch是能 捕获到 上游 抛出的异常, 异常的传递过程
.collect { println(it) }
3、异常总结
1、Channel是什么?
1、Channel可以用于协程间通信
// 通道Channel
val channel = Channel<Int>()
// 生产者
launch{
(1..6).forEach {
delay(1000L)
println("我生产了一个:$it")
channel.send(it)
}
}
// 消费者
launch{
(1..6).forEach {
val r= channel.receive()
println("消费了一个:$r")
}
}
1、生产速度>消费速度
// 通道Channel
val channel = Channel<Int>(Channel.UNLIMITED)
// 第一种发方式 消费
(1..8).forEach {
delay(2000L)
val r= channel.receive()
println("消费了一个:$r")
}
// 第二种发方式 消费
val it = channel.iterator()
while (it.hasNext()) {
val item = it.next()
delay(2000L)
println("消费了一个:$item")
}
// 第三种发方式 消费
for (item in channel) {
delay(2000L)
println("消费了一个:$item")
}
// 生产者的快捷方式
val produce = produce {
(1..20).forEach { delay(2000L) ; send(it) }
}
// 普通的消费
launch {
for (item in produce) {
println("消费了一个:$item")
}
}
// receive()接收数据,有数据没有消费,send会一直阻塞
launch {
println("消费了一个:${produce.receive()}")
delay(2000)
println("消费了一个:${produce.receive()}")
println("消费了一个:${produce.receive()}")
println("消费了一个:${produce.receive()}")
println("消费了一个:${produce.receive()}")
println("消费了一个:${produce.receive()}")
}
produce(capacity = 100),会增加缓冲区,只要没有放满send不会再阻塞。
// 消费者的快捷方式
val consumer = actor<Int> {
(1..20).forEach {
println("消费了一个:${receive()}")
}
}
// 普通的生成
launch {
(1..20).forEach { delay(2000L) ; consumer.send(it) }
}
1、channel.close
channel.close() 之前 isClosedForSend == false
channel.close() 之后 isClosedForSend == true
// 生产者
launch {
(1..6).forEach {
if (!channel.isClosedForSend) {
channel.send(it)
println("我生产了一个$it")
// if (it == 3) channel.close() // 大部分情况下,是生产者 去close
}
}
println("close前 isClosedForSend:${channel.isClosedForSend} " +
" isClosedForReceive:${channel.isClosedForReceive}")
channel.close()
println("close后 isClosedForSend:${channel.isClosedForSend} " +
" isClosedForReceive:${channel.isClosedForReceive}")
}
如果消费完了 isClosedForReceive == true, 否则就是false
如果缓冲区里面还有内容,没有消费完 也是 false
// 消费者
launch {
try {
for (i in channel) {
delay(2000L)
println("我消费了一个:$i")
}
}finally {
println("finally isClosedForSend:${channel.isClosedForSend} " +
" isClosedForReceive:${channel.isClosedForReceive}")
}
}
1、广播给所有消费者,多个地方可以接收到
val channel = Channel<Int>()
val broadcastChannel = channel.broadcast(Channel.BUFFERED)
// 生产者
launch {
repeat(8) {
delay(1000L)
broadcastChannel.send(it + 100001) // 发送
}
broadcastChannel.close() // 关闭
}
repeat(8) {
// 消费者
launch {
val r = broadcastChannel.openSubscription()
for (i in r) {
println("协程$it ---- 消费者 ${i}")
}
}
}
1、select: 择优选择数据,谁先返回用谁的
2、select 是一个用于多路选择的结构,可以同时等待多个挂起函数或通道的操作完成。它类似于 switch 或 if-else 的多路分支语句,但是它是用于协程的异步操作。
suspend fun selectExample() {
select<Unit> {
someChannel.onReceive { value ->
// 处理从通道接收到的值
}
someDeferred.onAwait { result ->
// 处理异步操作完成后的返回值
}
onTimeout(1000) {
// 在指定时间内没有任何操作完成时执行
}
}
}
3、select可以用于上游,也可以用于下游
data class Home(val info1: String, val info2: String)
data class HomeRequestResponseResultData(val code: Int, val msg: String, val home: Home)
// 请求本地加载首页数据
fun CoroutineScope.getHomeLocalData() = async (Dispatchers.IO) {
delay(3000)
Home("数据1...", "数据1...")
}
// 请求网络服务器加载首页数据
fun CoroutineScope.getHomeRemoteData() = async (Dispatchers.IO) {
delay(6000)
Home("数据3...", "数据4...")
}
launch {
val localRequestAction = getHomeLocalData()
val remoteRequestAction = getHomeRemoteData()
val resultResponse = select<HomeRequestResponseResultData> {
localRequestAction.onAwait {
// 做校验 工作
// ...
// 省略1000行代码
HomeRequestResponseResultData(200, "恭喜你,请求成功", it) // 最后一行作为返回值
}
remoteRequestAction.onAwait {
// 做校验 工作
// ...
// 省略1000行代码
HomeRequestResponseResultData(200, "恭喜你,请求成功", it) // 最后一行作为返回值
}
}
println("resultResponse:$resultResponse")
}
2、async需要在调用的CoroutineScope中执行
fun CoroutineScope.getHomeLocalData() = async (Dispatchers.IO) {
delay(3000)
Home("数据1...", "数据1...")
}
// 对CoroutineScope扩展
val channels = arrayOf(Channel<String?>(), Channel<String?>())
launch {
delay(6000)
channels[0].send("login successful")
}
launch {
delay(8000)
channels[1].send("register successful")
}
val receiveResult = select<String ?> {
for (channel in channels) {
channel.onReceive {
// 做校验 工作
// ...
// 省略1000行代码
"[$it]" // 最后一行作为返回值
}
}
}
println(receiveResult)
launch无返回值,但想看谁执行的最快
val job1 = launch {println("launch1 run")} // 无返回值
val job2 = launch {println("launch2 run")} // 无返回值
select<Unit> {
job1.onJoin { println("launch1 执行完成了 很快") }
job2.onJoin { println("launch2 执行完成了 很快") }
}
发送数据,并且显示回调的内容(上游)
// 准备Channel数组
val channels = arrayOf(Channel<Char>(), Channel<Char>())
// 协程一:Channel 的 发射源
launch(Dispatchers.Default) {
select<Unit> {
// 并行干活,send
launch {
channels[0].onSend('女') {
println("channels[0].onSend('女') { $it }")
}
}
// 并行干活,send
launch {
channels[1].onSend('男') {
println("channels[1].onSend('男') { $it }")
}
}
}
}
// 协程二:下游 接收阶段
launch { println("channel1 下游接收 ${channels[0].receive()}") }
launch { println("channel2 下游接收 ${channels[1].receive()}") }
输出:
channel1 下游接收 女
channels[0].onSend('女') { RendezvousChannel@34206005{EmptyQueue} }
// 1. onSend先发送消息
// 2. 下游接收到
// 3. onSend回调打印消息
it <= 0 <= times,回调action
public inline fun repeat(times: Int, action: (Int) -> Unit) {
contract { callsInPlace(action) }
for (index in 0 until times) {
action(index)
}
}
多个协程并发操作一个变量会出现安全问题
var i = 0
List(10000) {
GlobalScope.launch {
i ++
}
}
println(i)
// TODO 解决方案一:Java的API来解决
var i = AtomicInteger(0)
List(10000) {
GlobalScope.launch {
i.incrementAndGet()
}
}.joinAll()
println(i.get())
Channel是并发安全队列
val m = Mutex()
var i = 0
List(10000) {
GlobalScope.launch {
m.withLock { // withLock会自己在finally里解锁
i++
}
}
}.joinAll()
println(i)
加锁,并解锁
public suspend inline fun <T> Mutex.withLock(owner: Any? = null, action: () -> T): T {
lock(owner) // lock
try {
return action()
} finally { // unlock
unlock(owner)
}
}
信号量:
// TODO 解决方案四:KT的API来解决 信号量控制
val s = Semaphore(1) // Semaphore(1) 等价于 Mutex
var i = 0
List(10000) {
GlobalScope.launch {
s.withPermit { // acquire + release
i++
}
}
}.joinAll()
println(i)
public suspend inline fun <T> Semaphore.withPermit(action: () -> T): T {
acquire()
try {
return action()
} finally {
release()
}
}
不出现并发安全问题才是最好的
// 不变性
var i = 0
val r = i + List(10000) {// List
GlobalScope.async {
delay(1000) // 每个操作耗时1000ms
1
}
}
.map { it.await() }// 转为List
.sum()
println(r) // 总共1000ms后,获取所有的综合结果。并发安全的
如何知道协程执行结束1-2-3
invokeOnCompletion
1、任务同时进行,协作
// 生产手机
fun createPhones() = sequence <String> {
println("开始生产 华为P10")
GlobalScope.launch { delay(2000000) } //这个阻塞,是另一个作用域,不影响。
yield("华为P10")
println("开始生产 华为P20")
GlobalScope.launch { delay(2000000) }
yield("华为P20")
}
// 消费手机
fun getPhones(sequence : Sequence<String>) {
val phones = sequence.iterator()
var r = phones.next()
println("消费了一台 $r")
r = phones.next()
println("消费了一台 $r")
}
// 开始协作
getPhones(createPhones())
1、挂起函数的类型和普通函数的类型,完全是两个完全不同的
(Double)->String
suspend (Double)->String
2、suspend关键字会被kotlin处理成Continuation, Continuation == Callback,里面都是成功和失败的回调
//编译前
suspend fun query(id:Int):String{
withContext(Dispatchers.IO){
delay(1000)
}
return id.toString()
}
Java调用上面的挂起代码:并不会执行挂起逻辑,只会输出基本的状态
Object result = MyKtKt.query(1314, new Continuation<String>() {
@NonNull
@Override
public CoroutineContext getContext() {
return EmptyCoroutineContext.INSTANCE;
}
@Override
public void resumeWith(@NonNull Object o) {
System.out.println("resumeWith:" + o);
}
});
System.out.println("result:" + result);
输出:
result:COROUTINE_SUSPENDED
3、Continuation:保证后续代码的恢复功能
4、协程为什么不会阻塞任何线程?
代码和变量会拷贝出去,然后原有地方remove掉。方便恢复
5、为什么suspend方法外面需要suspend代码块包裹?
6、为什么非挂起函数不能调用挂起函数?
GlobalScope.launch(context = Dispatchers.Main) { // 默认是Default异步线程
// TODO >>>>>>>>>>>>>>>>>>>>>>>> t85:协程背后状态机原理 ① >>>>>>>>>>>>>>>>>>>>>>>>
// TODO 先执行 异步请求1
var serverResponseInfo = requestLoadUser()
tv?.text = serverResponseInfo // 更新UI
tv?.setTextColor(Color.GREEN) // 更新UI
// TODO 更新UI完成后,再去执行 异步请求2
serverResponseInfo = requestLoadUserAssets()
tv?.text = serverResponseInfo // 更新UI
tv?.setTextColor(Color.BLUE) // 更新UI
// TODO 更新UI完成后,再去执行 异步请求3
serverResponseInfo = requestLoadUserAssetsDetails()
tv?.text = serverResponseInfo // 更新UI
mProgressDialog?.dismiss() // 更新UI
tv?.setTextColor(Color.RED) // 更新UI
}
- 用自己实现的拦截功能的Continuation,传入用于切换线程的Dispatcher
- 异步请求1,resumeWith回调返回结果+更新UI。构造协程并启动startCoroutine
- 异步请求2,resumeWith回调返回结果+更新UI。构造协程并执行
- 异步请求3,resumeWith回调返回结果+更新UI。
// TODO >>>>>>>>>>>>>>>>>>>>>>>> t85:协程背后状态机原理 ② >>>>>>>>>>>>>>>>>>>>>>>>
requestLoadUser(DispatchedContinuation(object : Continuation<String> {
override val context: CoroutineContext
get() = EmptyCoroutineContext // 在这里我们破坏了协程的规则,所以这句话不生效
override fun resumeWith(result: Result<String>) {
var serverResponseInfo = result.getOrThrow()
tv?.text = serverResponseInfo // 更新UI
tv?.setTextColor(Color.GREEN) // 更新UI
suspend {
// TODO 更新UI完成后,再去执行 异步请求2
requestLoadUserAssets()
}.startCoroutine(object : Continuation<String> {
override val context: CoroutineContext
get() = Dispatchers.Main // 生效的 因为拦截器intercepted 会调用 Dispatchers.Main 切换到Main
override fun resumeWith(result: Result<String>) {
var serverResponseInfo = result.getOrThrow()
tv?.text = serverResponseInfo // 更新UI
tv?.setTextColor(Color.BLUE) // 更新UI
suspend {
// TODO 更新UI完成后,再去执行 异步请求3
requestLoadUserAssetsDetails() // 最后一行Lambda返回
}.createCoroutine(object: Continuation<String> {
override val context: CoroutineContext
get() = Dispatchers.Main // 生效的 因为拦截器intercepted 会调用 Dispatchers.Main 切换到Main
override fun resumeWith(result: Result<String>) {
var serverResponseInfo = result.getOrThrow()
tv?.text = serverResponseInfo // 更新UI
mProgressDialog?.dismiss() // 更新UI
tv?.setTextColor(Color.RED) // 更新UI
}
}).resumeWith(Result.success(Unit))
}
})
}
}, HandlerDispatcher))
1、下面是kotlin挂起和恢复代码,后面是反编译Java层面代码
fun main() = runBlocking{
val r1 = query(123)
println(r1) // 更新UI代码1
val r2 = query(456)
println(r2) // 更新UI代码2
val r3 = query(789)
println(r3) // 更新UI代码3
}
suspend fun query(id:Int):String{
delay(1000)
return id.toString()
}
// 简化
public final class MyKtKt {
//1、调用main方法
public static void main(String[] var0) {
main();
}
// 2、main,在runBlockinng包裹中运行
public static final void main() {
BuildersKt.runBlocking$default((CoroutineContext)null, (Function2)(new Function2((Continuation)null) {
// 3、invoke会被调用
public final Object invoke(Object var1, Object var2) {
return ((<undefinedtype>)this.create(var1, (Continuation)var2)).invokeSuspend(Unit.INSTANCE);
}
// 构造Function2:runBlocing相关的
public final Continuation create(@Nullable Object value, @NotNull Continuation completion) {
Intrinsics.checkNotNullParameter(completion, "completion");
Function2 var3 = new <anonymous constructor>(completion);
return var3;
}
// 4、invokeSuspend触发状态机:
// label:状态
// =====================================
// 【非挂起流程】:
// 调用query(123,this), 执行println(r1) // 更新UI代码1
// 调用query(456,this), 执行println(r2) // 更新UI代码2
// 调用query(789,this), 执行println(r3) // 更新UI代码3
//=====================================
// 【挂起流程】:
// 第一轮:状态 = 0,调用query(123, this), 返回COROUTINE_SUSPENDED结果(因为query也是挂起函数)。调度一段时间后,通过continuation resume进入第二轮。
// 第二轮:状态 = 1,执行更新UI代码1,调用query(456, this), 同上
// 第三轮:状态 = 2,执行更新UI代码2,调用query(789, this), 同上
// 第四轮: 状态 = 3,执行更新UI代码3 => 结束
//=====================================
int label;
@Nullable
public final Object invokeSuspend(@NotNull Object $result) {
Object result;
label26: {
label25: {
switch (this.label) {
case 0: // 第一轮
ResultKt.throwOnFailure($result);
this.label = 1;
result = MyKtKt.query(123, this); // query
if (result == COROUTINE_SUSPENDED) {
return COROUTINE_SUSPENDED; // COROUTINE_SUSPENDED 挂起标志
}
break;
case 1: // 第二轮
ResultKt.throwOnFailure($result);
result = $result;
break; // 恢复执行第一个挂起方法后面的代码
case 2: // 第三轮
ResultKt.throwOnFailure($result);
result = $result;
break label25; // 恢复执行第二个挂起方法后面的代码
case 3:
ResultKt.throwOnFailure($result);
result = $result;
break label26; // 恢复执行第三个挂起方法后面的代码
default:
throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
}
String r1 = (String)result;
System.out.println(r1); // 恢复执行
this.label = 2;
result = MyKtKt.query(456, this); // 第三轮入口
if (result == COROUTINE_SUSPENDED) {
return COROUTINE_SUSPENDED;
}
} // label = 2时,跳转到这里
String r2 = (String)result;
System.out.println(r2); // 恢复
this.label = 3;
result = MyKtKt.query(789, this); // 第四轮入口
if (result == COROUTINE_SUSPENDED) {
return COROUTINE_SUSPENDED;
}
}
String r3 = (String)result; // 恢复
System.out.println(r3);
return Unit.INSTANCE;
}
}), 1, (Object)null);
}
// =====================================
// 5、query挂起函数解析
// 内部调用delay挂起函数。
// 1. 挂起函数外部为第一轮
// 2. delay为第二轮
//
// 流程:
// 1. 第一轮:进入初始化,并且执行挂起方法,传入continuation
// 2. 恢复:continuation恢复调用invokeSuspend,进入第二轮
// 3. 第二轮:执行业务逻辑,返回结果给上层
//=====================================
@Nullable
public static final Object query(int id, Continuation continutation) {// id = 入参123、456、789
Object $continuation;
label20: { // STEP 1: 第一轮:初始化
if (continutation instanceof <undefinedtype>) { // 内部包含label
$continuation = (<undefinedtype>)continutation;
if ((((<undefinedtype>)$continuation).label & Integer.MIN_VALUE) != 0) { // 0:初始化完成
((<undefinedtype>)$continuation).label -= Integer.MIN_VALUE; // label = 0
break label20;
}
}
$continuation = new ContinuationImpl(var1) { // ContinuationImpl 会持有之前协程的执行结果和当前协程的执行结果。
Object result;
int label;
int I$0;
@Nullable
public final Object invokeSuspend(@NotNull Object $result) {
this.result = $result;
this.label |= Integer.MIN_VALUE;
return MyKtKt.query(0, this); // 第二轮进入
}
};
}
Object $result = ((<undefinedtype>)$continuation).result;
switch (((<undefinedtype>)$continuation).label) {
case 0: // STEP 2: 第一轮,
ResultKt.throwOnFailure($result);
((<undefinedtype>)$continuation).I$0 = id; // id = 入参123、456、789
((<undefinedtype>)$continuation).label = 1;
// 延迟1000ms,返回COROUTINE_SUSPENDED标记
if (DelayKt.delay(1000L, (Continuation)$continuation) == COROUTINE_SUSPENDED) {
return COROUTINE_SUSPENDED;
}
break;
case 1:
// STEP 3:第二轮进入,Continuaiton resume后,获取结果,并且返回
id = ((<undefinedtype>)$continuation).I$0;
ResultKt.throwOnFailure($result);
break;
default:
throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
}
return String.valueOf(id);
}
}
1、核心:
协程本质 = continuation + 状态机
2、Kotlin协程是如何实现挂起和恢复的?底层思想是什么? ====> 状态
3、源码实现核心机制:
4、为什么不会阻塞主线程?
5、Continuation的三个状态
6、挂起函数和非挂起函数在状态机里面的流程
invokeSuspendd的状态扭转是什么意思?
KT内部不让外部使用:
/** TODO >>>>>>>>>>>>>>>>>>>>>>>> 协程背后状态机原理 >>>>>>>>>>>>>>>>>>>>>>>>
* Kotlin 源码中的 ContinuationImpl 无法直接访问,
* 为防止报错,将 ContinuationImpl 拷贝了部分代码出来
*/
abstract class ContinuationImpl(
completion: Continuation<Any?>?,
private val _context: CoroutineContext?
) : Continuation<Any?> {
constructor(completion: Continuation<Any?>?) : this(completion, completion?.context)
public override val context: CoroutineContext
get() = _context!!
@Transient
private var intercepted: Continuation<Any?>? = null
public fun intercepted(): Continuation<Any?> = intercepted ?: (context[ContinuationInterceptor]?.interceptContinuation(this) ?: this).also { intercepted = it }
public final override fun resumeWith(result: Result<Any?>) { }
abstract fun invokeSuspend(result: Result<Any?>): Any? // 抽象方法定义
}
枚举类:
// 枚举类定义
enum class CoroutineSingletons { COROUTINE_SUSPENDED, UNDECIDED, RESUMED }
showCoroutine是我们写的同名方法,增加了Continuaiton参数
fun showCoroutine(continuation: Continuation<Any?>): Any? {
class ShowContinuation(continuation: Continuation<Any?>) : ContinuationImpl(continuation) {
// 表示协程状态机当前的状态
var label = 0
// 协程返回结果
var result: Any? = null
// 用于保存之前协程的计算结果
var user: Any? = null
var userAssets: Any? = null
// invokeSuspend 是协程的关键
// 它最终会调用 showCoroutine(this) 开启协程状态机
// 状态机相关代码就是后面的 when 语句
// 协程的本质,可以说就是 Continuation + 状态机 完成的
override fun invokeSuspend(_result: Result<Any?>): Any? {
result = _result
label = label or Int.Companion.MAX_VALUE
return showCoroutine(this)
}
}
val continuationState: ShowContinuation = if (continuation is ShowContinuation) {
continuation
} else {
ShowContinuation(continuation) // 如果是初次运行,会把continuation: Continuation作为参数传递进去
}
// 三个变量,对应原函数的三个变量
lateinit var user: String
lateinit var userAssets: String
lateinit var userAssetsDetails: String
// result 接收协程的运行结果
var result = continuationState.result
// suspendReturn 接收挂起函数的返回值
var suspendReturn: Any? = null
// CoroutineSingletons 是个枚举类
// COROUTINE_SUSPENDED 代表当前函数被挂起了
val sFlag = CoroutineSingletons.COROUTINE_SUSPENDED
var loop = true
while (loop) {
// 协程状态机核心代码
when (continuationState.label) {
0 -> {
// 检测异常
throwOnFailure(result)
println("开始执行了哦")
// 将 label 置为 1,准备进入下一次状态
continuationState.label = 1
// 执行 requestLoadUser
suspendReturn = requestLoadUser(continuation) // withContent(IO)执行耗时任务
// 判断是否挂起
if (suspendReturn == sFlag) {
return suspendReturn // suspend挂起了,执行耗时任务完成后,返回suspendReturn == 加载到[用户数据]信息集
} else {
result = suspendReturn // 未挂起,result 接收协程的运行结果
// 马上进入下一个专题
}
}
1 -> {
// 检测异常
throwOnFailure(result)
// 获取 user 值
user = result as String
toast("更新UI:$user")
// 将协程结果存到 continuation 里
continuationState.user = user
// 准备进入下一个状态
continuationState.label = 2
// 执行 requestLoadUserAssets
suspendReturn = requestLoadUserAssets(continuation) // withContent(IO)执行耗时任务
// 判断是否挂起
if (suspendReturn == sFlag) {
return suspendReturn // suspend挂起了,执行耗时任务完成后,返回suspendReturn == 加载到[用户资产数据]信息集
} else {
result = suspendReturn // 未挂起,result 接收协程的运行结果
// 马上进入下一个专题
}
}
2 -> {
throwOnFailure(result)
user = continuationState.user as String
// 获取 friendList userAssets 的值
userAssets = result as String
toast("更新UI:$userAssets")
// 将协程结果存到 continuation 里
continuationState.user = user
continuationState.userAssets = userAssets
// 准备进入下一个状态
continuationState.label = 3
// 执行 requestLoadUserAssetsDetails
suspendReturn = requestLoadUserAssetsDetails(continuation) // withContent(IO)执行耗时任务
// 判断是否挂起
if (suspendReturn == sFlag) {
return suspendReturn // suspend挂起了,执行耗时任务完成后,返回suspendReturn == 加载到[用户资产详情数据]信息集
} else {
result = suspendReturn // 未挂起,result 接收协程的运行结果
// 马上进入下一个专题
}
}
3 -> {
throwOnFailure(result)
user = continuationState.user as String
userAssets = continuationState.userAssets as String
userAssetsDetails = continuationState.result as String
toast("更新UI:$userAssetsDetails")
loop = false
}
}
}
return Unit
}
private fun requestLoadUser(completion: Continuation<String>): Any? {
// no implement
// 调用 invokeSuspend
thread {
try {
Thread.sleep(3000L) // 这里的睡眠,模拟了请求网络时的耗时操作中...
if (completion is ContinuationImpl) {
completion.invokeSuspend(Result.success("加载到[用户数据]信息集")) // 状态流转
}
} catch (e: Exception) {
e.printStackTrace()
completion.resumeWithException(Throwable("加载[用户数据],加载失败,服务器宕机了 e:$e"))
}
}
return CoroutineSingletons.COROUTINE_SUSPENDED // 代表requestLoadUser是真正的挂起了 挂起函数
}
private fun requestLoadUserAssets(completion: Continuation<String>): Any? {
// no implement
// 调用 invokeSuspend
thread {
try {
Thread.sleep(4000L) // 这里的睡眠,模拟了请求网络时的耗时操作中...
if (completion is ContinuationImpl) {
completion.invokeSuspend(Result.success("加载到[用户资产数据]信息集"))
}
} catch (e: Exception) {
e.printStackTrace()
completion.resumeWithException(Throwable("加载[用户资产数据],加载失败,服务器宕机了 e:$e"))
}
}
return CoroutineSingletons.COROUTINE_SUSPENDED // 代表requestLoadUserAssets是真正的挂起了 挂起函数
}
private fun requestLoadUserAssetsDetails(completion: Continuation<String>): Any? {
// no implement
// 调用 invokeSuspend
thread {
try {
Thread.sleep(5000L) // 这里的睡眠,模拟了请求网络时的耗时操作中...
if (completion is ContinuationImpl) {
completion.invokeSuspend(Result.success("加载到[用户资产详情数据]信息集"))
}
} catch (e: Exception) {
e.printStackTrace()
completion.resumeWithException(Throwable("加载[用户资产详情数据],加载失败,服务器宕机了 e:$e"))
}
}
return CoroutineSingletons.COROUTINE_SUSPENDED // 代表requestLoadUserAssets是真正的挂起了 挂起函数
}
1、协程拦截器是什么?
public fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T>
2、自定义实现和理解拦截器
使用DispatcherContinuation作为Continuation就能拦截,并且切换线程,执行恢复后的代码。
// 拦截器接口
public interface Dispatcher{
fun dispatch(run : ()->Unit)
}
// Andorid Handler
object HandlerDispatcher : Dispatcher{
var handler = Handler(Looper.getMainLooper()!!)
override fun dispatch(run: () -> Unit) {
handler.post(run)
}
}
// 拦截器中转站(等价于createCoroutineUnintercepted(completion).intercepted())
class DispatcherContinuation<T>(val continuation:Continuation<T>, val dispatcher: Dispatcher):Continuation<T>{
override val context: CoroutineContext
get() = continuation.context
override fun resumeWith(result: Result<T>) {
dispatcher.dispatch {
continuation.resumeWith(result)
}
}
}
使用处:
query(123, DispatcherContinuation(object : Continuation<String> {
override val context: CoroutineContext
get() = EmptyCoroutineContext // 本来可以用来切换线程,模拟场景下报废了。
override fun resumeWith(result: Result<String>) {
println(result.getOrNull()) // 回调结果输出
}
}, HandlerDispatcher))
自己模拟实现的挂起函数:
// 子线程sleep 1000ms后,回调结果
fun query(id: Int, continuation: Continuation<String>): Unit {
thread {
Thread.sleep(1000)
val result = id.toString()
continuation.resumeWith(Result.success(result))
}.start()
}
探究后流程总结
1、Dispatchers.IO
// 1、默认IO调度器
public val IO: CoroutineDispatcher = DefaultIoScheduler
// 2、实现抽象类:ExecutorCoroutineDispatcher
internal object DefaultIoScheduler : ExecutorCoroutineDispatcher(), Executor
// 3、继承自抽象类:CoroutineDispatcher
public abstract class ExecutorCoroutineDispatcher: CoroutineDispatcher(), Closeable
// 4、继承接口:ContinuationInterceptor
public abstract class CoroutineDispatcher :
AbstractCoroutineContextElement(ContinuationInterceptor), ContinuationInterceptor
// 5、ContinuationInterceptor定义的接口:核心interceptContinuation
public interface ContinuationInterceptor : CoroutineContext.Element {
companion object Key : CoroutineContext.Key<ContinuationInterceptor>
public fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T>
}
2、CoroutineDispatcher:定义dispatch,实现interceptContinuation,构造DispatchedContinuation
public abstract fun dispatch(context: CoroutineContext, block: Runnable)
public final override fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T> =
DispatchedContinuation(this, continuation) // 构造DispatchedContinuation
3、interceptContinuation的作用是什么?
// 1、intercepted()内部
createCoroutineUnintercepted(completion).intercepted()
// 2、扩展Continuaiton:intercepted()方法,调用ContinuationImpl定义并实现的intercepted()
public actual fun <T> Continuation<T>.intercepted(): Continuation<T> = (this as? ContinuationImpl)?.intercepted() ?: this
// 3、ContinuationImpl.kt的intercepted()会调用CoroutineDispatcher实现的interceptContinuation()
public fun intercepted(): Continuation<Any?> =
intercepted ?: (context[ContinuationInterceptor]?.interceptContinuation(this) ?: this).also { intercepted = it }
4、DispatchedContinuation
// 1. 参数一:dispatcher分发器
// 2. 参数二:原Continuation,用于包裹
internal class DispatchedContinuation<in T>(
val dispatcher: CoroutineDispatcher,
val continuation: Continuation<T>
) : DispatchedTask<T>(MODE_UNINITIALIZED), CoroutineStackFrame, Continuation<T> by continuation {
override fun resumeWith(result: Result<T>) {
val context = continuation.context
// 用分发器,dispatch// 比如获取到Dispatchers.IO
dispatcher.dispatch(context, this) //【step 2】 // 这里的this指的是Runnable,为什么有Runnable?实现的抽象类DispatchedTask,最顶层是Runnable
}
}
// IO为例>>>>>>>>>>>>>>>>注意下面流程都是IO线程池的样例
public val IO: CoroutineDispatcher = DefaultIoScheduler
object DefaultIoScheduler : ExecutorCoroutineDispatcher(), Executor {
override fun dispatch(context: CoroutineContext, block: Runnable) {
default.dispatch(context, block) // default分发 //【step 3】
}
}
private val default = UnlimitedIoScheduler.limitedParallelism(xxx) // 单例类UnlimitedIoScheduler的方法
// UnlimitedIoScheduler
private object UnlimitedIoScheduler : CoroutineDispatcher() {
override fun dispatch(context: CoroutineContext, block: Runnable) {
// 用 DefaultScheduler,分派
DefaultScheduler.dispatchWithContext(block, BlockingContext, false) // 【step 7】
}
}
// CoroutineDispatcher实现的
public open fun limitedParallelism(parallelism: Int): CoroutineDispatcher {
return LimitedDispatcher(this, parallelism)
}
// LimitedDispatcher:实现了Runnable可以理解为...
class LimitedDispatcher(
val dispatcher: CoroutineDispatcher,
val parallelism: Int
) : CoroutineDispatcher(), Runnable, Delay by (dispatcher as? Delay ?: DefaultDelay){
override fun dispatch(context: CoroutineContext, block: Runnable) {
dispatchInternal(block) { //【step 4】
dispatcher.dispatch(this, this)//【step 6】
}
}
private inline fun dispatchInternal(block: Runnable, dispatch: () -> Unit) {
/**===添加到queue中==========================
*private fun addAndTryDispatching(block: Runnable): Boolean {
* queue.addLast(block)
* return runningWorkers >= parallelism
*}
*========================================*/
if (addAndTryDispatching(block)) return //【step 5】
if (!tryAllocateWorker()) return
dispatch() // 一层层调到DefaultScheduler : SchedulerCoroutineDispatcher的dispatchWithContext,再到CoroutineScheduler的dispatch加入到队列中。
}
override fun run() {
while (true) { // while()真正实现了状态机的多次调用
val task = queue.removeFirstOrNull() // ====从queue中取出====
task.run() // task封装的block:Runnable是上层的DispatchedTask,run()会调用resumeWith
}
}
}
DefaultScheduler源码:
// 1、继承自SchedulerCoroutineDispatcher
object DefaultScheduler : SchedulerCoroutineDispatcher
// 2、定义了线程池的参数
internal open class SchedulerCoroutineDispatcher(
private val corePoolSize: Int = CORE_POOL_SIZE,
private val maxPoolSize: Int = MAX_POOL_SIZE,
private val idleWorkerKeepAliveNs: Long = IDLE_WORKER_KEEP_ALIVE_NS,
private val schedulerName: String = "CoroutineScheduler",
) : ExecutorCoroutineDispatcher() {
override val executor: Executor
get() = coroutineScheduler
private var coroutineScheduler = createScheduler()
private fun createScheduler() = //【step 1】
CoroutineScheduler(corePoolSize, maxPoolSize, idleWorkerKeepAliveNs, schedulerName)
// 构建CoroutineScheduler
// 3、交给coroutineScheduler分发,是构建的CoroutineScheduler
override fun dispatch(context: CoroutineContext, block: Runnable): Unit = coroutineScheduler.dispatch(block)
// coroutineScheduler的dispatch分发
internal fun dispatchWithContext(block: Runnable, context: TaskContext, tailDispatch: Boolean) {
coroutineScheduler.dispatch(block, context, tailDispatch) // 【step 8】
}
}
internal class CoroutineScheduler(
@JvmField val corePoolSize: Int,
@JvmField val maxPoolSize: Int,
@JvmField val idleWorkerKeepAliveNs: Long = IDLE_WORKER_KEEP_ALIVE_NS,
@JvmField val schedulerName: String = DEFAULT_SCHEDULER_NAME
) : Executor, Closeable {
internal inner class Worker private constructor() : Thread() {
override fun run() = runWorker()
private fun runWorker() {
while(xxx){
val task = findTask(mayHaveLocalTasks) // 队列取出任务,开始执行
executeTask(task) // task.run() //
tryPark() // 等待唤醒
}
}
}
fun dispatch(block: Runnable, taskContext: TaskContext = NonBlockingContext, tailDispatch: Boolean = false) {
val task = createTask(block, taskContext)
val currentWorker = currentWorker()
// 投递到Worker内部
// localQueue.add(task, fair = tailDispatch)
val notAdded = currentWorker.submitToLocalQueue(task, tailDispatch)
// 内部 tryUnpark(),会唤醒runWorker中等待唤醒的任务
signalBlockingWork(skipUnpark = skipUnpark)
}
private fun signalBlockingWork(skipUnpark: Boolean) {
if (tryCreateWorker(stateSnapshot)) return
tryUnpark() // Try unpark again in case there was race between permit release and parking
}
private fun tryCreateWorker(state: Long = controlState.value): Boolean {
// 比较核心线程数,决定是否创建新线程
if (cpuWorkers < corePoolSize) {
val newCpuWorkers = createNewWorker()
}
return false
}
private fun createNewWorker(): Int {
synchronized(workers) {
// 创建Worker,执行线程
val worker = Worker(newIndex)
worker.start() // start会调用run,run会调用runWorker()
return cpuWorkers + 1
}
}
}
DispatchedTask源码:
internal abstract class DispatchedTask<in T>(public var resumeMode: Int) : SchedulerTask() {xxx}
internal actual typealias SchedulerTask = Task
// 核心在于类型是Runnable,就必然有run
internal abstract class Task(var submissionTime: Long,var taskContext: TaskContext) : Runnable {xxx}
// 探究DispatchedTask的run
public final override fun run() {
val taskContext = this.taskContext
val delegate = delegate as DispatchedContinuation<T> // delegate交给子类DispatchedContinuation去实现了,返回的结果是this
val continuation = delegate.continuation // DispatchedContinuation的参数二:continuation
withContinuationContext(continuation, delegate.countOrElement) {
//........................................................
continuation.resume(getSuccessfulResult(state)) // 执行上层的resume
}
}
会返回一个Continuation,并且创建协程与之关联,调用resume进行恢复
1、自己实现挂起函数:
/**=====================================
* 底层原理剖析
*=====================================*/
// 手动创建suspend 方法
val suspendLambda : suspend ()->String = suspend {
"User Information"
}
// 2、获取结果的回调
val reusltContinuation = object:Continuation<String>{
override val context: CoroutineContext
get() = Dispatchers.IO // 会使用,suspendCoroutine的continuaiton,不会破坏结构,可以使用Dispatchers
// 会有内置拦截器:SafeContinuation(createCoroutineUnintercepted(completion).intercepted(), COROUTINE_SUSPENDED)
// 拦截器会正确使用CoroutineContext,并且使用Dispatchers
override fun resumeWith(result: Result<String>) {
println("get result = ${result.getOrNull()}")
}
}
// 3、创建协程本体,但还未执行
val continuation : Continuation<Unit> = suspendLambda.createCoroutine(reusltContinuation)
// 4、启动协程
continuation.resumeWith(Result.success(Unit))
2、suspend + Continuation + createCoroutine + resume 链式实现
suspend {
getUserInfo("wch")
}.createCoroutine(object : Continuation<String>{
override val context: CoroutineContext
get() = EmptyCoroutineContext
// 空的上下文
// 协程会在调用它的线程上执行
override fun resumeWith(result: Result<String>) {
println("Thread:${Thread.currentThread().name}")
// 输出结果
println("get result = ${result.getOrNull()}")
}
}).resume(Unit)
3、createCoroutine源码
public fun <T> (suspend () -> T).createCoroutine(completion: Continuation<T>): Continuation<Unit> =
SafeContinuation(createCoroutineUnintercepted(completion).intercepted(), COROUTINE_SUSPENDED)
intercepted()
4、探究createCoroutine和协程执行逻辑
// 1、suspend{}内部是协程需要执行的lambda代码块,如下的block
// public inline fun suspend(noinline block: suspend () -> R): suspend () -> R = block
val continuationBenti : Continuation<Unit> = suspend {
println("suspend{} thread:${Thread.currentThread().name}")
delay(2000L)
// 假设这里做了很多工作
// ...
// 1000000
requestAction()
}
// 2、Continuation的实现,上面block()返回结果要通过 context切换线程,resumeWith回调上抛
// 3、createCoroutine构造出【协程本体】
.createCoroutine(object: Continuation<Int> {
override val context: CoroutineContext
get() = Dispatchers.Default
override fun resumeWith(result: Result<Int>) {
println("resumeWith result:${result} thread:${Thread.currentThread().name}")
}
})
// 4、【协程本体】真正执行
continuationBenti.resume(Unit)
1、startCoroutine相比于createCoroutine会调用resume
2、startCoroutine不需要safeContinuation包裹,为什么?
已经明确了resume只会执行一次
用suspendCancellableCoroutine实现suspend挂起函数
- suspendCancellableCoroutine 获得可以取消的CoroutineContext
- resume:上传成功或者失败的业务数据
- resumeWithException:出现异常时
- invokeOnCancellation:可以在协程执行完毕后调用
open class ResponseBean
data class Successful(val code: Int, val msg: String, val data: String) : ResponseBean()
data class Error(val code: Int, val msg: String, val data: String) : ResponseBean()
suspend fun requestLogin(userName: String , userPwd: String) : ResponseBean
= suspendCancellableCoroutine { continuation: CancellableContinuation<ResponseBean> ->
continuation.invokeOnCancellation {
println("本次协程执行完毕,做清理回收工作 ... $it")
/*xxx.close()
xxx.release()
xxx.清理工作*/
// ...
}
try {
if ("Derry" == userName && "123456" == userPwd) {
continuation.resume(Successful(200, "恭喜,请求登录成功", "data{success}"))
} else {
continuation.resume(Error(404, "不恭喜,登录失败了", "data{error}"))
}
} catch (e:Exception) {
continuation.resumeWithException(Throwable("请求发送了异常:$e"))
} finally {
continuation.cancel(Throwable("cancel"))
}
}
调用方法
fun main() = runBlocking <Unit> {
GlobalScope.launch {
val result = requestLogin("Derry", "123456")
println("请求结果是:${result}")
}.join()
}
手写挂起函数:获得Continuation
suspend fun getName(id:Int):String = suspendCoroutine<String> {
thread(start = true){
println("正在做耗时操作...")
it.resume("$id:feather")
} // 用参数start来启动,不要自己start
println("真正读取中...")
}
// 下面:是收起来的方式,上面:展开的方式。没有区别
suspend fun getName2(id: Int) : String {
withContext(Dispatchers.IO) {
delay((5000L..20000L).random())
}
return "$id:feather" // 调用Continuation回调 最终的成果长度 给 用户
}
startCoroutine创建协程体,启动协程:底层真正的样子,回调形式。
suspend{
getName(10)
}.startCoroutine(object: Continuation<String> {
override val context: CoroutineContext
get() = EmptyCoroutineContext // 空上下文,会用上面suspend{}包裹产生的Context
override fun resumeWith(result: Result<String>) {
println("接收到返回数据:${result.getOrNull()}") // 执行完suspend方法后回调结果
}
})
回调形式直接变成同步形式:底层状态机在发力
// 等价于GlobalScope.launch
GlobalScope.launch {
val result = getName(123) // Kotlin编译器 做了海量的处理,看不到
println(" 案例一 读完完成 长度是:${result}")
}
1、挂起函数类型
suspend fun getLength(str:String):Int{
delay(1000)
return str.length
}
// 1、常规类型
var funType1:suspend (String)->Int = ::getLength
// 2、编译后的类型,写代码不支持(suspend处理为Continuation)
//var funType2: (String, Continuation)->Any? = ::getLength
println(funType1.invoke("feather"))
// 3、可以编译过的形式
var funType3: (String, Continuation<Int>)->Any? = ::getLength as (String, Continuation<Int>)->Any?
2、type3为什么返回的结果是Any?而不是Int?
val result = funType3.invoke(xxx)
println(result)
输出结果: COROUTINE_SUSPENDED
1、将suspend函数用as关键字转换为编译后类型,照样调用
suspend fun getLength(str:String):Int = suspendCoroutine<Int>{
object :Thread(){
override fun run() {
super.run()
it.resume(str.length)
}
}.start()
}
fun main() {
// 3、使用编译后类型,运行OK,证明无问题
var funType3: (String, Continuation<Int>)->Any? = ::getLength as (String, Continuation<Int>)->Any?
funType3.invoke("feather", object:Continuation<Int>{
override fun resumeWith(result: Result<Int>) {
println(result.getOrNull())
}
override val context: CoroutineContext
get() = EmptyCoroutineContext
})
}
2、suspendCoroutine是什么?
1、为什么系统Continuation需要有context和resumeWith?作用是什么?
public interface Continuation<in T> {
public val context: CoroutineContext
public fun resumeWith(result: Result<T>)
}
context: 当前场景主要是CoroutineDispatcher,用于线程切换
2、自己实现IContinuation
interface IContinuation<T>{
val context:Dispatcher // Dispatcher是自定义的分发器,类似CoroutineDispatchers
fun resume(result:T)
}
3、自己函数使用自定义Continuation和系统版本
fun getLength(str: String, iContinuation: IContinuation<Int>) {
thread {// 网络请求的时候,切换到 异步线程
// ...耗时操作
// ...分发结果
iContinuation.context.dispatch { // 切换回主线程main
iContinuation.resume(str.length) // 调用Continuation回调 最终的成果长度 给 用户
}
}
println("真正读取中...")
}
// 使用官方的Continuation
suspend fun getLength(str: String) = suspendCoroutine<Int>{
thread { // 网络请求的时候,切换到 异步线程
// ...耗时操作
// ...分发结果
it.resume(str.length) // 返回结果
}
println("真正读取中...")
}
4、使用两个版本的挂起函数
// 自定义Continuation
getLength("Derry",object : IContinuation<Int> {
override val context: Dispatcher
get() = HandlerDispatcher
override fun resume(result: Int) {
// 输出最终结果
}
})
// 官方框架层 API
suspend { getLength("Derry2") }.startCoroutine(object : Continuation<Int> {
override val context: CoroutineContext
get() = Dispatchers.Main
override fun resumeWith(result: Result<Int>) {
// 输出结果
}
})
1、startCoroutine
// 1、本质构造出ContinuationImpl
// 2、再调用intercepted(),获得拦截器包裹Continuation
// 3、resume,内部是resumeWith
public fun <T> (suspend () -> T).startCoroutine(
completion: Continuation<T>
) {
// 很明显调用了intercepted()
createCoroutineUnintercepted(completion).intercepted().resume(Unit)
}
2、createCoroutineFromSuspendFunction: 实现invokeSuspend
// createCoroutineUnintercepted的内部实现在IntrinsicsJvm.kt
createCoroutineFromSuspendFunction(probeCompletion) {
// this是外面调用startCoroutine的Function1 var1
(this as Function2<R, Continuation<T>, Any?>).invoke(receiver, it)
}
// 底层是:createCoroutineFromSuspendFunction(continuation)
private inline fun <T> createCoroutineFromSuspendFunction(
completion: Continuation<T>,
crossinline block: (Continuation<T>) -> Any?
): Continuation<Unit> {
// 拿到上层suspend状态机部分的context(决定协程在哪儿执行)
val context = completion.context
return object : ContinuationImpl(completion, context) {
private var label = 0
// 内部实现invokeSuspend方法,状态机,调用block(this),也是上层suspend{}包裹生成的代码
override fun invokeSuspend(result: Result<Any?>): Any? =
when (label) {
0 -> {
label = 1
block(this)
}
1 -> {
label = 2
}
else -> error("This coroutine had already completed")
}
}
}
3、ContinuationImpl:实现intercepted(),构建拦截器
// 底层是ContinuationImpl
abstract class ContinuationImpl(xxx) : BaseContinuationImpl(completion){
public fun intercepted(): Continuation<Any?> =
intercepted
?: (context[ContinuationInterceptor]?.interceptContinuation(this) ?: this)
.also { intercepted = it }
}
4、ContinuationImpl继承自BaseContinuationImpl:实现了resumeWith,会调用invokeSuspend
internal abstract class BaseContinuationImpl(val completion: Continuation<Any?>?) : Continuation<Any?>, CoroutineStackFrame, Serializable {
public final override fun resumeWith(result: Result<Any?>) {
// while无限循环,不停的调用
while (true) {
// 1、调用实现的invokeSuspend
val outcome = invokeSuspend(param)
// 2、挂起函数直接retuern
if (outcome === COROUTINE_SUSPENDED) return
completion.resumeWith(outcome)
}
}
}
// 定义:是invokeSuspend的开端
protected abstract fun invokeSuspend(result: Result<Any?>): Any?
}
// 层层找到实现了Continuation接口
public interface Continuation<in T> {
public val context: CoroutineContext
public fun resumeWith(result: Result<T>)
}
intercepted()直接获取到Dispatchers
override val context: CoroutineContext
get() = Dispatchers.Main // 这里的部分
//
val Main: MainCoroutineDispatcher get() = MainDispatcherLoader.dispatcher
// 继承自CoroutineDispatcher
MainCoroutineDispatcher : CoroutineDispatcher()
// CoroutineDispatcher有抽象接口
public abstract fun dispatch(context: CoroutineContext, block: Runnable)
1、线程切换的原理
override fun resumeWith(result: Result<T>) {
dispatcher.dispatch {
continuation.resumeWith(result)
}
}
override fun dispatch(run: () -> Unit) {
handler.post(run) // 内部执行的是continuation.resumeWith
}
suspend fun request1() = suspendCoroutine<Int> {
}
// suspendCancellableCoroutine相比于suspendCoroutine:
// 多了invokeOnCancellation 取消时回调
suspend fun request2() = suspendCancellableCoroutine<Int> {
it.invokeOnCancellation {
}
}
底层核心都是:suspendCoroutineUninterceptedOrReturn
// 编译器生成代码的 // 内建生成
public suspend inline fun <T> suspendCoroutineUninterceptedOrReturn(crossinline block: (Continuation<T>) -> Any?): T {
throw NotImplementedError("Implementation of suspendCoroutineUninterceptedOrReturn is intrinsic")
}
suspendCoroutineUninterceptedOrReturn生成的代码是什么?
就是对使用协程的代码进行反编译后,生成的代码如:(伪代码)
suspend inline fun <T> suspendCoroutineUninterceptedOrReturn(crossinline block: (Continuation<T>) -> Any?): T {
// 创建一个 Continuation 对象
val completion = object : Continuation<T> {
override val context: CoroutineContext = EmptyCoroutineContext
override fun resumeWith(result: Result<T>) {
// 处理协程恢复的逻辑
// ...
}
}
// 调用传入的 lambda,传递 Continuation 对象
val value = block(completion)
// 根据 lambda 的返回值类型进行处理,返回结果或抛出异常
if (value != COROUTINE_SUSPENDED) {
@Suppress("UNCHECKED_CAST")
value as T
} else {
COROUTINE_SUSPENDED
}
}
public suspend inline fun <T> suspendCoroutine(crossinline block: (Continuation<T>) -> Unit): T {
return suspendCoroutineUninterceptedOrReturn { c: Continuation<T> ->
val safe = SafeContinuation(c.intercepted()) // 包裹 ====> 包装者模式
block(safe) // 一样用
safe.getOrThrow() // 获取结果作为返回值
}
}
1、作用
// actual关键字
internal actual class SafeContinuation<in T>
internal enum class CoroutineSingletons { COROUTINE_SUSPENDED, UNDECIDED, RESUMED }
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iXsNce5p-1692500679708)(…/…/images/34bc697e00fa00a056dbb568cbbbf3ecaae203fef7c5e0fb869509b9b1fe59e6.png)]
如何决定是挂起还是非挂起?
根据挂起函数返回的结果Any?是不是CoroutineSingletons.COROUTINE_SUSPENDED
var funType3: (String, Continuation<Int>)->Any? = ::getLength as (String, Continuation<Int>)->Any?
挂起函数:返回挂起标记
非挂起函数:返回值
====> OkHttp
也分同步还是异步,调用enqueue和execute。
execute是直接获取到结果,返回。
fun main() {
val continuationBenti: Continuation<Unit> = suspend {
delay(2000L)
1000000
}.createCoroutine(object : Continuation<Int> {
override val context: CoroutineContext
get() = Dispatchers.Default
override fun resumeWith(result: Result<Int>) {
println("resumeWith result:${result} thread:${Thread.currentThread().name}")
}
})
continuationBenti.resume(Unit)
}
kotlin构造出的代码分为三大部分
// STEP 1: 构造出suspend对应部分(状态机部分代码)
Function1 var1 = new Function1((Continuation)null) {xxx};
// STEP 2: 构造Continuation,在后面获得结果的时候切换到Dispatchers的指定线程,执行resumeWith
Continuation continuationBenti = createCoroutine(var1, new Continuation() {xxx});
// STEP 3: 驱动协程执行
continuationBenti.resumeWith(xxx);
Function1代码
Function1 var1 = (Function1)(new Function1((Continuation)null) {
int label;
public final Object invokeSuspend(@NotNull Object $result) {
Object var2 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
switch (this.label) {
case 0:
ResultKt.throwOnFailure($result);
this.label = 1;
if (DelayKt.delay(2000L, this) == var2) {
return var2;
}
break;
case 1:
ResultKt.throwOnFailure($result);
break;
}
return Boxing.boxInt(1000000);
}
@NotNull
public final Continuation create(@NotNull Continuation completion) {
Intrinsics.checkNotNullParameter(completion, "completion");
Function1 var2 = new <anonymous constructor>(completion);
return var2;
}
// invoke会调用自己的invokeSuspend
public final Object invoke(Object var1) {
return ((<undefinedtype>)this.create((Continuation)var1)).invokeSuspend(Unit.INSTANCE);
}
});
请参考《拦截器源码分析》的代码关于拦截器流程
// >>>>>>>>>>>> 【第一轮】 >>>>>>>>>>>>>>>>>>>>>>>>>>
// >>>>>>>>>>>> 跳转到目标线程执行业务的流程 >>>>>>>>>>>>>>>>>>>>>>>
1. resumeWith会调用拦截器DispatchedContinuation的resumeWith
2. 会执行dispatcher分发器的dispatch系列任务
3. 创建Worker线程,启动,while循环中从任务队列取任务,无任务就park
4. 将dispatch任务,投递到Worker线程的任务队列,并且unpark
// >>>>>>>>>>>> Runnable的run,到上层Kotlin编译器实现的,invoke、invokeSuspend >>>>>>>>>>>>>>>>>>
1. Worker执行的是DispatchedContinuation的父类DispatchedTask的run(),执行上层的resume()->resumeWith()
2. resumeWith由BaseContinuationImpl实现,内部调用invokeSuspend
3. invokeSuspend由ContinuationImpl的匿名内部类实现,会调用生成的FunctionX的invoke,invoke会调用上层kotlin为协程代码块生成的invoke,内部是上层invokeSuspend
4. kotlin用状态机实现invokeSuspend,进入第一轮,发现是挂起函数直接返回COROUTINE_SUSPENDED
// >>>>>>>>>>>> 运行结果给上层,切换到目标协程执行 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
1. 获取到原先线程如主线程,投递到Handler中执行,resumeWith(实现了结果返回后切换线程执行后续代码)
// >>>>>>>>>>>> 【第二轮】 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
// 再投递任务调目标线程Worker中执行
// 执行invokeSuspend方法
// 切换回原先线程,resumeWith返回返回值。
// >>>>>>>>>>>> 【第三轮】重复 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
简单语言描述下:
resume是调用拦截器的resumeWith,进行dispatch,内部流程,到线程池中,找到Worker线程,并且投递到内部的local队列中,执行unpark会唤醒,worker线程的park,worker线程会while循环中循环处理local队列中的任务,没有任务就park。执行任务就是指执行封装好的Task内部的RUnnable的run方法。
run方法中,执行上层block这个上层实现的lambda方法,实现了线程切换。然后执行到上面挂起函数,被kotlin编译器生成的Function的invoke方法,invoke会调用invokeSuspend方法,这里面就是根据我们的多次挂起恢复的代码,生成的状态机,利用状态翻转,触发反复进入invokeSuspend方法。实现拆解地狱回调。
在每一invokeSuspend完成一个挂起任务,并且执行好操作需要返回数据时。根通过拦截器,切换到我们需要回复的线程,假如是Android的UI线程,内部dispatch就是通过handler的post进行切换,然后执行resume方法,一层层传递到COntinuation实现的remsueWith代码,这段代码就是我们上层用户希望恢复后会执行的代码。
@RestrictsSuspension // 会限定对DerryScope的扩展函数,必须使用DerryScope的函数
class DerryScope
suspend fun DerryScope.suspendAction():Int = 200
fun test(lambda: (suspend DerryScope.() -> Int)){
lambda.createCoroutine(DerryScope(), object: Continuation<Int>{
override val context: CoroutineContext
get() = Dispatchers.Default
override fun resumeWith(result: Result<Int>) {
println("resumeWith ${result.getOrNull()}")
}
}).resume(Unit)
}
// 调用,像系统的launch一样
test{
suspendAction() // 必须调用DerryScope的扩展函数
// suspendAction2() // 错误!不可以
}
各个企业主流:Retrofit+OkHttp+RxJava+Glide
主流框架:
一些框架都是:协程+JetPack全家桶+mvvm
理想:协程+JetPack全家桶+mvvm+Retrofit+OkHttp+Glide
xxx未来五年 mvi + compose
为什么不直接View->Model层?
ViewModel管理所有的LiveData数据的状态的稳定性
横竖屏切换等操作,数据会消失
LiveData关联布局,产生DataBinding
面试题:协程中 Dispatchers.IO,但是我却想让他在 main线程跑协程,有没有办法?
答:CoroutineStart.UNDISPATCHED
特点:由于没有调度中心调度,所以拿不到 Dispatchers.IO 干脆就用 在当前调用栈线程中执行协程,所以是main线程跑协程
ThreadLocalEventLoop
EventLoop
ContinuationInterceptor
BlockingCoroutine
joinBlocking()