目录
- 协程的相关概念
- 协程的上下文
- Job
- Dispatchers
- CoroutineName
- 协程的作用域
- 协程的管理
- 协程的启动
- 父子协程的概念
- Android特有的作用域的使用和区别
- 异常管理
- 协程的常见使用和封装
协程的相关概念
协程是并发式的设计模式,简化异步执行。
- suspend 修饰符协程的关键字,被suspend修饰的方法都必须再另一个suspend函数或者Coroutine协程程序中进行调用
- Continuation 隐藏回调,异步写法转换成同步写法
- Job 控制协程的生命周期,帮助协程间进行结构性并发
- Dispatcher 指定协程的运行线程或临时切换线程都属于调度的作用
- CoroutineContext 协程的上下文,包含用户定义的一些数据集合,类似map集合,key来获得不同的数据。协程上下文可以控制协程、调度协程、捕获异常等
- CoroutineScope 划定协程的作用域
协程的生命周期
协程的状态查询
- isActive
- isCompleted
- isCancelled
常用的协程操作:
- cancel 用于Job的取消,取消协程,job.cancel()
- start 用于启动一个协程,让其到达Active状态 jon.start()
- invokeOnCompletion 添加一个监听,当工作完成或者异常时会调用 job.invokeOnCompletion()
- join 阻塞并等候当前协程完成
协程的上下文
Job Dispatchers CoroutineName都实现了Element接口,如果需要结合不同的CoroutineContext,可以通过plus来拼接。
Job 协程启动返回的对象,可以进行cancel join等操作,因为也是实现了CoroutineContext,一样可以用于启动协程的构造中。
var job: Job? = null
job = GlobalScope.launch(Dispatchers.Main) {
LogUtils.w("执行在协程中...")
delay(1000L)
LogUtils.w("执行完毕...")
}
override fun onDestroy() {
job?.cancel()
super.onDestroy()
}
或者通过构造传入协程中
GlobalScope.launch(job.run { Job() }) {
LogUtils.w("执行在协程中...")
delay(1000L)
LogUtils.w("执行完毕...")
}
override fun onDestroy() {
job?.cancel()
super.onDestroy()
}
var job: Job = Job()
GlobalScope.launch(job) {
LogUtils.w("执行在协程中...")
delay(1000L)
LogUtils.w("执行完毕...")
}
override fun onDestroy() {
job?.cancel()
super.onDestroy()
}
Dispatchers 协程的调度
不管是指定协程的运行线程,还是临时切换线程,运行完毕会切换回来,都是通过 Dispatchers 来调度的。常用的线程调度为:
- Dispatchers.Main Android主线程
- Dispatchers.Unconfined 当前CoroutineScope的线程策略
- Dispatchers.Default 默认值,为JVM共享线程池
- Dispatchers.IO IO线程池,默认为64个线程
CoroutineName 可以成为协程命名
协程的作用域
GlobalScope,还有要一些常见的CoroutineScope对象
- GlobalScope 全局范围 不会自动结束执行
- MainScope 主线程作用域,全局范围
- lifecycleScope 生命周期范围内,用于Activity组件
- viewModelScope viewModel范围内,用于ViewModel中,在ViewModel被回收时会自动结束
runBlocking 与 coroutineScope 的主要区别在于后者在等待所有子协程执行完毕时不会阻塞当前线程。
suspend挂起函数是不会阻塞线程的,它只会挂起协程,而不阻塞线程。
为什么需要CoroutineScope ?
- 将任务放在作用域呢,要取消就都取消,要完成就都完成
- 父协程的取消子协程也就取消啦
- GlobalScope是无法取消的,MainScope是可以取消的。
- GlobalScope并没有Job对象,所以也就没有cancel此协程。
- viewModelScope 只能在ViewModel中使用,绑定ViewModel的生命周期。需要添加依赖库
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.0'
- lifecycleScope只能在Activity、Fragment中使用,会绑定Activity和Fragment的生命周期。依赖库
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.5.0'
协程的管理
协程的启动
- launch{}CoroutineScope的扩展方法,启动一个协程,不阻塞当前协程,并返回新协程的Job。
- async{} CoroutineScope的扩展方法,启动一个协程,不阻塞当前协程,返回一个Deffer,除包装了未来的结果外,其余特性与launch{}一致
- runBlocking{} 是一个裸方法,创建一个协程,并阻塞当前线程,直到协程执行完毕。前面说过,这里不再赘述。
- withContext(){}一个suspend方法,在给定的上下文执行给定挂起块并返回结果,它并不启动协程,只会(可能会)导致线程的切换。用它执行的挂起块中的上下文是当前协程的上下文和由它执行的上下文的合并结果。
withContext的目的不在于启动子协程,它最初用于将长耗时操作从UI线程切走,完事再切回来。 - coroutineScope{} 一个suspend方法,创建一个新的作用域,并在该作用域内执行指定代码块,它并不启动协程。其存在的目的是进行符合结构化并发的并行分解(即将长耗时任务拆分为并发的多个短耗时任务,并等待所有并发任务完成后再返回)。
总结:async是异步执行,withContext是同步执行。
阻塞和非阻塞指是否阻塞其他协程。launch的是非阻塞的,runBlocking就是阻塞的,它会阻止其他协程的运行。runBlocking 会阻塞通协程域内的其他协程
GlobalScope.launch{
val deferred = async{
}
val response = deferred.await()
valr result = withContext(Dispatcher.IO){
delay(1000L)
return@withContext "1234"
}
}
GlobalScope.launch{//主线程中调用
LogUtils.w("执行在协程中...")
//doSomeThing()
GlobalScope.launch(Dispatchers.IO) {
//异步执行
LogUtils.w("异步执行result1")
delay(1000L)
LogUtils.w("result1:1234")
}
GlobalScope.launch(Dispatchers.IO) {//子线程中调用以下方法
//异步执行
LogUtils.w("异步执行result2")
delay(1000L)
LogUtils.w("result2:123456")
}
LogUtils.w("执行完毕...")
}
// 输出日志
执行在协程中
异步执行result1
异步执行result2
执行完毕
result1:1234
result2:123456
异常管理
CoroutineExceptionHandler 协程异常处理
private val exceptionHandler = CoroutineExceptionHandler { _:CoroutineContext, throwable: Throwable->
LogUtils.e(throwable.message ?: "Unkown Error")
}
GlobalScope.launch(exceptionHandler) {
doSomeThing()
}
增加扩展方法
fun CoroutineScope.safeLaunch(onError: ((Throwable) -> Unit)? = null,
onBlock: () -> Unit) {
val exceptionHandler = CoroutineExceptionHandler { _, throwable ->
one rror?.invoke(throwable)
}
this.launch(exceptionHandler) {
onBlock.invoke()
}
}
协程的常见使用和封装
launch(结果是先执行外部方法,再执行内部方法,非阻塞式) ,runBlocking(结果是先执行内部方法,再执行外部方法,阻塞式), withContext ,async/await
sealed class Result<out T : Any> {
data class Success<out T : Any>(val data: T? = null) : Result<T>()
data class Error(val code: Int, val error: String) : Result<Nothing>()
override fun toString(): String {
return when (this) {
is Success<*> -> "请求成功 - data: $data"
is Error -> "请求失败 - code:$code, error: $error"
}
}
}
suspend fun <T : Any> executeCal(call: suspend () -> Result<T>): Result<T> {
return kotlin.runCatching { call() }.getOrElse { handleException(it) }
}
private fun handleException(e: Throwable): Result.Error {
val error = when (e) {
is CancellationException -> "请求取消"
is SocketTimeoutException -> "连接超时"
is JsonParseException -> "数据解析错误"
is NumberFormatException -> "数据类型转换错误"
else -> "请求失败,请稍后再试"
}
e.printStackTrace()
return Result.Error(-1, error)
}
原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/282929.html