Log.v("zx", "结果:$result")
}
打印:
com.z.zjetpack V/zx: sleep
com.z.zjetpack V/zx: sleep
com.z.zjetpack V/zx: 结果:默认数据
如上,如果在1秒内完成了,那么结果为 完成,如果没做完会返回结果null,为null即显示默认数据
[]( )协程的异常处理
---------------------------------------------------------------------
runBlocking {
val job1 = launch {
try {
throw NullPointerException()
} catch (e: Exception) {
log("launch,$e.toString()")
}
}
val job2 = async {
try {
throw NullPointerException()
} catch (e: Exception) {
log("async,$e.toString()")
}
}
job2.await()
}
异常的传播特性是:当一个协程生成异常,它会传给它的父级,之后父级会取消它自己的子级,然后取消它自己,最后将异常传给它的父级。
那么如果我们想要一个子协程发送异常不影响其他协程怎么办呢?
答:使用SupervisorJob和SupervisorScope
使用SupervisorJob时,一个子协程的运行失败不会影响到它的子协程。SupervisorJob不会传播异常给它的父级,他会让子协程自己处理异常
使用CoroutineExceptionHandler捕获协程异常
val handle = CoroutineExceptionHandler { coroutineContext, throwable ->
Log.v("zx","$throwable")
}
CoroutineScope(Dispatchers.Main).launch(handle) {
throw NullPointerException()
}
Android种全局异常处理
全局异常处理器可以获取到所有协程未处理的未捕获异常,不管它并不能对异常进行捕获,虽然不能阻止程序崩溃,全局异常处理器在程序调试和异常上报场景有很大作用。
我们需要在app/src/main下面创建一个resources/META-INF/services目录并在其中创建一个名为kotlinx.coroutines.CoroutineExceptionHandler的文件,文件内容就是异常处理器的全类名。
![在这里插入图片描述](https://img-blog.csdnimg.cn/a194c4dd63aa490fae9a495510d114f3.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAWlpfR08=,size_20,color_FFFFFF,t_70,g_se,x_16)
package com.z.zjetpack.coroutine
import android.util.Log
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlin.coroutines.CoroutineContext
class GException:CoroutineExceptionHandler {
override val key = CoroutineExceptionHandler
override fun handleException(context: CoroutineContext, exception: Throwable) {
Log.v("zx","异常信息:$exception")
}
}
kotlinx.coroutines.CoroutineExceptionHandler文件中的内容为:包名+类名
com.z.zjetpack.coroutine.GException
![在这里插入图片描述](https://img-blog.csdnimg.cn/199f7f8a2e704dd79e007075f8191c23.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAWlpfR08=,size_20,color_FFFFFF,t_70,g_se,x_16)
### []( )取消与异常
* 取消与异常紧密相关,协程内部使用CancellationException来取消,这个异常会被忽略,当子协程被取消时,不会取消它的父协程
runBlocking {
val job = launch {
val childjob = launch {
try {
delay(Long.MAX_VALUE)
}finally {
Log.v("zx","子协程被取消了")
}
}
//出让执行权,让子协程有机会执行
yield()
Log.v("zx","开始取消")
childjob.cancelAndJoin()
//childjob.cancel()
//没有AndJoin就会继续往下执行
//Log.v("zx","取消中。。。")
yield()
Log.v("zx","父协程还没被取消")
//父协程中释放资源
...
}
job.join()
}
打印:
com.z.zjetpack V/zx: 开始取消
com.z.zjetpack V/zx: 子协程被取消了
com.z.zjetpack V/zx: 父协程还没被取消
* 如果一个协程遇到了CancellationException以外的异常,它将使用该异常取消它的父协程。当父协程的所有子协程都结束后,异常才会被父协程处理。
runBlocking {
val handle = CoroutineExceptionHandler { coroutineContext, throwable ->
Log.v("zx","捕获异常:$throwable")
}
val job1 = GlobalScope.launch(handle) {
val child1 = launch {
try {
delay(Long.MAX_VALUE)
}finally {
//这里如果要执行挂起函数要用NonCancellable
withContext(NonCancellable) {
Log.v("zx","child1子协程已被取消,但异常未被处理")
delay(100)
Log.v("zx","child1子协程已完成")
}
}
}
val child2 = launch {
delay(10)
Log.v("zx","child2 抛出异常")
throw NullPointerException()
}
}
job1.join()
}
打印:
com.z.zjetpack V/zx: child2 抛出异常
com.z.zjetpack V/zx: child1子协程已被取消,但异常未被处理
com.z.zjetpack V/zx: child1子协程已完成
com.z.zjetpack V/zx: 捕获异常:java.lang.NullPointerException
### []( )异常聚合
当协程的多个子协程因为异常而失败时,一般取第一个异常处理,在第一个异常后发生的所有异常都会绑定到第一个异常上。
runBlocking {
val handle = CoroutineExceptionHandler { coroutineContext, throwable ->
Log.v("zx", "其他异常:${throwable.suppressed.contentToString()}")
Log.v("zx", "当前捕获异常:$throwable")
}
GlobalScope.launch(handle) {
launch {
try {
delay(Long.MAX_VALUE)
} finally {
throw NullPointerException()
}
}
launch {
try {
delay(Long.MAX_VALUE)
} finally {
throw IndexOutOfBoundsException()
}
}
launch {
delay(100)
throw ArithmeticException()
}
}
}
打印:
其他异常:[java.lang.NullPointerException, java.lang.IndexOutOfBoundsException]
当前捕获异常:java.lang.ArithmeticException
[]( )(三)Flow异步流
========================================================================
[]( )flow介绍
--------------------------------------------------------------------
挂起函数可以异步返回单个值,那如何**异步多次**返回多个值呢?
使用flow,flow的特点:
* flow{…}块中的代码可以挂起
* 使用flow,suspend修饰符可以省略
* 流使用emit函数发射值
* 流使用collect的函数收集值
* flow类似冷流,flow中代码直到流被收集(调用collect)的时候才运行,类似lazy,什么时候用,什么时候执行。
* 流的连续性:流收集都是按顺序收集的
* flowOn可更改流发射的上下文,即可以指定在主线程或子线程中执行
* 与之相对的是热流,我们即将介绍的 StateFlow 和 SharedFlow 是热流,在垃圾回收之前,都是存在内存之中,并且处于活跃状态的。
//使用flow,suspend修饰符可以省略
fun doflow() = flow<Int> {
for (i in 1..5) {
//这里是挂起,不是阻塞
delay(500)
emit(i)
}
}.flowOn(Dispatchers.IO)
//调用
runBlocking {
doflow().collect {
log("value=$it")
}
}
打印(多次返回多个值)
com.z.zjetpack V/zx: value=1
com.z.zjetpack V/zx: value=2
com.z.zjetpack V/