2024年协程的取消和异常Part4-不应取消的协程_viewmodelscope,2024年最新大厂面试必备技能考什么

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!


img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上鸿蒙开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

需要这份系统化的资料的朋友,可以戳这里获取

如果你需要运行的某个操作的生命周期长于app进程(例如向服务器发送日志),那么,请使用WorkManager。WorkManager是用于预期在未来某个时间点执行的关键操作的库。

只要app进程还活着,那么协程就可以一直运行。对于那些需要在当前进程生命周期内有效,并且在用户杀掉app时可以取消的操作,就使用协程(例如,发起一个网络请求获取新闻列表数据)。

那些在协程中不应该取消的操作

假如,我们的应用中有一个ViewModel和一个Repository,其逻辑如下:

class MyViewModel(private val repo: Repository) : ViewModel() {
  fun callRepo() {
    viewModelScope.launch {
      repo.doWork()
    }
  }
}
class Repository(private val ioDispatcher: CoroutineDispatcher) {
  suspend fun doWork() {
    withContext(ioDispatcher) {
      doSomeOtherWork()
      veryImportantOperation() // 这个操作不应该被取消,它非常重要
    }
  }
}


我们不希望veryImportantOperation()被viewModelScope控制,因为它可以在任何时候被取消。我们希望该操作比viewModelScope生命周期更长。我们怎么才能做到这一点?

为此,请在Application类中创建自己的Scope,并在由它启动的协程中调用这些重要的操作。哪些类需要用到该Scope,直接从Application中取就行了。

与我们稍后将看到的其他解决方案(如GlobalScope)相比,创建自己的CoroutineScope的好处是你可以根据需要对其进行配置。比如:你可以配置一个CoroutineExceptionHandler,将自己的线程池用作Dispatcher等,将所有常见的配置放在它的CoroutineContext中,非常方便。

你可以将其称为applicationScope,并且它必须包含一个SupervisorJob()以便协程中的异常不会在层次结构中传播(如本系列的第3篇文章中所示)。

class MyApplication : Application() {
  // No need to cancel this scope as it'll be torn down with the process
  //不需要取消该Scope,因为它会随着进程死亡而终止。
  val applicationScope = CoroutineScope(SupervisorJob() + otherConfig)
}


我们不需要取消该scope,因为我们希望只要应用程序进程还活着,它就保持活跃状态,所以我们不持有对SupervisorJob的引用。我们可以使用这个scope来运行协程,这些协程通常需要一个比调用处(比如ViewModel、Activity、Fragment等)更长的生命周期。

对于不应取消的操作,请从Application中创建CoroutineScope,然后用该CoroutineScope创建协程来调用它们。

每当你创建一个新的Repository实例时,请传入我们在上面创建的applicationScope。

使用哪个协程构造器?launch or async?

根据veryImportantOperation()的行为,你需要根据需要使用launch或async启动一个新的协程:

  • 如果你需要返回结果,那么使用async并调用await等待它完成
  • 如果没有,请使用launch,等待它完成可以使用join。如本系列第3篇文章所示,你必须在launch中手动处理异常

下面是你将使用launch启动协程的方式:

class Repository(
  private val externalScope: CoroutineScope,
  private val ioDispatcher: CoroutineDispatcher
) {
  suspend fun doWork() {
    withContext(ioDispatcher) {
      doSomeOtherWork()
      externalScope.launch {
        //如果这里可能会抛异常,那么请用try.catch把这里包起来,或者定义一个CoroutineExceptionHandler在externalScope的CoroutineContext中
        veryImportantOperation()
      }.join()
    }
  }
}


或者你使用async:

class Repository(
  private val externalScope: CoroutineScope,
  private val ioDispatcher: CoroutineDispatcher
) {
  suspend fun doWork(): Any { // Use a specific type in Result
    withContext(ioDispatcher) {
      doSomeOtherWork()
      return externalScope.async {
        //调用await时会暴露异常,异常将在调用doWork的协程中传播。如果调用doWork处的协程已经cancel,把me该异常将被忽略。
        veryImportantOperation()
      }.await()
    }
  }
}


在ViewModel中用viewModelScope调用了上面的doWork后,在任何情况下,都不会影响externalScope的执行,即使viewModelScope被破坏。此外,doWork()在veryImportantOperation()完成之前不会返回,就像任何其他suspend函数调用一样。

能不能稍微简单一点?

另一种使用方式是用withContext,然后将veryImportantOperation()包在externalScope的context中:

class Repository(
  private val externalScope: CoroutineScope,
  private val ioDispatcher: CoroutineDispatcher
) {
  suspend fun doWork() {
    withContext(ioDispatcher) {
      doSomeOtherWork()
      withContext(externalScope.coroutineContext) {
        veryImportantOperation()
      }
    }
  }
}


然而,使用这种方式有些地方需要注意一下:

  • 如果调用doWork的协程在执行veryImportantOperation()时被取消,它将一直执行到下一个退出节点,而不是在veryImportantOperation()执行完成之后
  • 当在withContext中使用context时,externalScope中的CoroutineExceptionHandler就不起作用了,异常将被重新抛出
替代方案

其实还有一些其他的方式可以让我们使用协程来实现这一行为。不过,这些解决方案不是在任何条件下都能有条理地实现。下面就让我们看看一些替代方案,以及为何适用或者不适用,何时使用或者不使用它们。

❌ GlobalScope

这里有几个原因,为什么你不应该使用GlobalScope:

  • 诱导我们写出硬编码值:直接使用GlobalScope可能会让我们倾向于写出硬编码的Dispatchers,这是一种很差的实践方式。
  • 它使测试变得非常困难:由于你的代码将在不受控制的scope中执行,因此你将无法管理由它启动的协程的执行
  • 你不能像我们对applicationScope所做的那样,为作用域中的所有协程都建立一个通用的CoroutineContext传递给GlobalScope启动所有的协程。

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!


img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上鸿蒙开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

需要这份系统化的资料的朋友,可以戳这里获取

资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上鸿蒙开发知识点,真正体系化!**

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

需要这份系统化的资料的朋友,可以戳这里获取

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值