Kotlin:lifecycleScope与GlobalScope以及MainScope的区别,详细分析为什么在Android中推荐使用lifecycleScope!

目录简要GlobalScopeMainScope()lifecycleScopelifecycleScope剩余问题分析(感兴趣的可以继续看)简要首先简要介绍一下kotlin协程作用域的三种类型。类型产生方式异常传播特征顶级作用域GlobalScope创建异常不向外传播。异常到达顶级作用域后,如果还没有被处理,会抛给当前的exceptionHandler,如果没有则给当前线程的uncaughtExceptionHandler协同作用域Job嵌套、coroutineSc
摘要由CSDN通过智能技术生成

原文地址➡️➡️➡️➡️➡️➡️➡️

简要

首先简要介绍一下kotlin协程作用域的三种类型。

类型 产生方式 异常传播特征
顶级作用域 GlobalScope创建 异常不向外传播。异常到达顶级作用域后,如果还没有被处理,会抛给当前的exceptionHandler,如果没有则给当前线程的uncaughtExceptionHandler
协同作用域 Job嵌套、coroutineScope创建 异常双传播。异常会向上向下双向传播。
主从作用域 可通过supervisorScope创建,另外MainScope和lifecycleScope内部设置了 异自上而下单项传播。父协程不去受理子协程产生的异常。但是一旦父布局出现了异常,则会直接取消子协程。

相关引用,kotlin协程库这里使用的版本是:1.4.2,可点击查看了解目前自己当前kotlin版本对应的协程库版本

project.ext.kotlin_coroutines_version = "1.4.2"
//kotlin协程标准库  GlobalScope
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_coroutines_version"
//kotlin协程Android支持  MainScope()
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines_version"
//lifecycle
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.3.0"

GlobalScope

GlobalScope继承自CoroutineScope。
kotlin协程标准库里面是没有MainScope以及lifecycleScope这些花里胡哨的东西的😯,一般使用GlobalScope.launch来启动协程即可。

val job = GlobalScope.launch {
   
    // TODO: 2021/8/29 do
}

使用launch启动可以设置启动模式,调度器以及异常处理器Handler,如下所示:

val coroutineExceptionHandler = CoroutineExceptionHandler {
    coroutineContext, throwable ->
    throwable.printStackTrace()
}
val job = GlobalScope.launch(
    Dispatchers.Main + coroutineExceptionHandler,
    CoroutineStart.DEFAULT
) {
   
    // TODO: 2021/8/29 do
}

调度器

调度器 线程,使用场景
Default 线程池,适合cpu密集的操作,比如计算
Main UI线程 比如计算
Io 线程池,适合io操作,比如网络请求等
Unconfined 不调度,直接执行。最后执行的线程取决于挂起函数恢复的时候的调度器

异常拦截器
设置方式,如上图所示,如果运行过程中挂起函数或者协程体体内部抛出异常(没有被处理),则会最终调用到异常拦截器。
协同作用域首先会看父协程是否处理异常,如果不处理才检查自己是否存在异常处理器等操作。同时会将异常下自己的子协程传递,取消子协程的进行。
顶级作用域会直接进行检查自己是否存在异常处理器等操作。
主从作用域其实就是协同作用域,但是它默认不处理子协程的异常,所以子协程只能处理,则就显示出,异常向下传播的镜像。不会影响自己的其他执行模块。比较适用于UI驱动的程序。比如说Android。

启动模式

启动模式 功能特性
DEFAULT 立即开始调度协程体,调度前若取消则直接取消
ATOMIC 立即开始调度协程体,直到第一个挂起点之前不能取消
LAZY 只有在需要(start/join/await)时才开始调度。其实创建协程有两种方式,一种是createCoroutine().resume(Unit);另一种是startCoroutine()内部调用了resume。而对于lazy的模式来说是先调用了createCoroutine,然后在需要的时候调用了resume启动协程
UNDISPATCHED 立即在当前协程执行协程体,知道遇到第一个挂起点(后续的执行线程取决于挂起点恢复执行时候的调度器)

好了,现在说一说GlobalScope的问题🙄。

  • 如果是Android基本都要调度到主线程进行操作,但是GlobalScope.launch默认的调度器是Default。每次都要显示的写Main不是很方便。
  • 上面也说到了Android这种UI驱动的程序,比较适合主从作用域,但是GlobalScope是顶级作用域。那有人就说了supervisorScope启动的不是主从作用域吗?但是supervisorScope是一个挂起函数(可自行查看源码。其实是内部引用了一个私有的SupervisorCoroutine类,继承自ScopeCoroutine。重写了childCancelled方法,就干了一件事,返回了false。简单来说就是不处理子协程的异常🤦‍♀️,这可忒不负责任了😢)。如果要调用supervisorScope必须要在一个协程或者挂起函数内。啊 ~ 这。那我不得先启动一个协程?😅
  • 还要一个问题,内存泄漏的问题。需要在页面销毁的适合取消掉当前协程。对于GlobalScope来说,就必须进行手动调用cancel的操作了。emmm,这波操作不仅麻烦而且危险(万一忘了取消咋整)!

MainScope()

先看一下源码:

public fun MainScope(): CoroutineScope = ContextScope(SupervisorJob() + Dispatchers.Main)

老话说得好啊,柿子的挑软的捏!Dispatchers.Main

  • 18
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

pumpkin的玄学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值