控制coroutine生命周期的方法

64 阅读5分钟

你有一个工作要做的时间!

allf1.in/the-dominan…

Coroutines是Android应用开发和Ktor中执行后台进程的现代方式。在我们的coroutine系列的一篇文章中,我们试图了解coroutines的基本知识。如果你还没有读过,可以在这里看一下。

在这篇文章中,我们将对这个问题进行一些基本的理解。

  • 为什么要控制coroutine的生命周期?
  • 控制coroutine生命周期的方法是什么?

如果你还没有选择,现在是时候开始了。

为什么要控制冠词的生命周期?

考虑这样一个场景:我们为某个进程启动了一个coroutine,但是用户离开了屏幕,这个进程不再需要了。在这种情况下,我们的coroutine将仍然运行,直到它完成。

另一种情况是,我们的最大执行时间限制是3秒,但进程花了更长时间,我们的进程目的现在已经失效了。

还有一种情况是,一些计算正在无限期地进行,我们需要由于任何原因退出。

在上述所有情况以及更多类似的情况下,我们需要一些机制来控制我们的程序继续进行。不这样做可能会泄漏我们的资源,或者导致我们的应用程序崩溃或不稳定。

这就给我们带来了下一个问题。

控制coroutine生命周期的方法?

使用Job

我们知道,coroutine是通过coroutine生成器启动的。coroutine生成器返回给我们一个job对象。这个工作对象的类型是 工作 类型,可以用来取消该循环程序。

runBlocking {
    Log.e("TAG","starting runBlocking")
    val job = launch {
        Log.e("TAG","starting launch block")
        repeat(1000) {
            Log.e("TAG","$it")
            delay(500)
        }
    }
    delay(2000)
    job.cancelAndJoin()
    Log.e("TAG","job cancelled")
}

Log.e("TAG","Printing normally on thread ${this.javaClass.name}")

如上所示,使用job对象,我们取消了作业,然后等待其完成。运行上述代码的结果是

2022-06-23 22:50:31.571 21243-21243/com.aqua30.parallelprocessingcoroutines E/TAG: starting runBlocking
2022-06-23 22:50:31.572 21243-21243/com.aqua30.parallelprocessingcoroutines E/TAG: starting launch block
2022-06-23 22:50:31.572 21243-21243/com.aqua30.parallelprocessingcoroutines E/TAG: 0
2022-06-23 22:50:32.072 21243-21243/com.aqua30.parallelprocessingcoroutines E/TAG: 1
2022-06-23 22:50:32.573 21243-21243/com.aqua30.parallelprocessingcoroutines E/TAG: 2
2022-06-23 22:50:33.074 21243-21243/com.aqua30.parallelprocessingcoroutines E/TAG: 3
2022-06-23 22:50:33.586 21243-21243/com.aqua30.parallelprocessingcoroutines E/TAG: job cancelled
2022-06-23 22:50:33.587 21243-21243/com.aqua30.parallelprocessingcoroutines E/TAG: Printing normally on thread MainActivity

我们启动了一个作业,将0.5秒的延迟重复1000次。这个作业是相对于它的父级coroutine同时启动的,它的延迟为2秒,然后它取消了这个作业。下面是事件发生的过程。

  • 首先启动runBlocking
  • 然后启动与父级代码同步的启动块
  • 父代码等待了2秒,同时作业执行了4次0到3。
  • 父代码取消了这个作业,并等待它安全完成并退出
  • 作业被取消
  • 返回到主线程

这就是我们如何使用job对象来控制我们的coroutine执行。

使用合作取消

取消一个循环程序应该是合作的,这意味着任何暂停的计算都应该在执行时检查该循环程序是否被取消。如果一个循环程序正在计算而没有检查是否取消,那么它就不能被取消。让我们举一个例子,如下

val job = launch {
    Log.e("TAG","starting launch block")
    while (true) {
        Log.e("TAG","don't run me. i'll eat all the memory")
    }
}
delay(2000)
job.cancelAndJoin()

运行这个作业并取消它不会停止我们的循环程序,因为它没有检查它被调用的循环程序是否被取消。它将继续运行。

为了解决这个问题,我们需要通过检查coroutine的取消状态来确保每个计算都是可以取消的,如下所示。

val job = launch {
    Log.e("TAG","starting launch block")
    while (isActive) {  // the flag provided by coroutineScope
        Log.e("TAG","don't run me. i'll eat all the memory")
    }
}
delay(2000)
job.cancelAndJoin()

现在,我们已经添加了一个 isActive 标志,该标志由coroutine范围本身提供。一旦作业被取消,这个标志将停止执行。

在某些情况下,如果作业被取消了,我们需要执行一些步骤,我们可以在 ***finally {}***块中进行,因为被取消的作业会抛出 CancellationException 这是在coroutines中的一个预期行为。这就是为什么我们在这里不需要catch块。

val job = launch {
    Log.e("TAG","starting launch block")
    try {
        repeat(1000) {
            Log.e("TAG","$it")
            delay(500)
        }
    } finally {
        Log.e("TAG","do something on cancellation")
    }
}
delay(2000)
job.cancelAndJoin()

使用超时

我们可能需要取消一个作业,因为它超过了它的时间限制。Coroutine提供了withTimeout函数,它为我们做了同样的事情。它抛出 超时注销异常在超过时间限制时抛出。

runBlocking {
    Log.e("TAG","starting runBlocking")
    withTimeout(1000) {
        Log.e("TAG","starting timeout")
        delay(2000)
    }
}

如果你不需要这个异常,可以使用函数 使用超时或空 它在超时时返回null。

Bamn!我们现在知道了如何管理我们的循环程序执行,并对我们的后台任务进行更精细的控制。执行这些代码并玩一玩,以便更好地理解。

这就是目前的全部内容!请继续关注!