当其 activity 协程作业被取消时取消 CountDownTimer

Cancel CountDownTimer when its activity coroutine job is cancelled

我已将协程范围分配给这样的 activity 生命周期

class MainActivity : AppCompatActivity(),
    CoroutineScope
{
    override val coroutineContext: CoroutineContext =
        Dispatchers.Main + SupervisorJob()
    ...
    
    override fun onDestroy() {
        coroutineContext[Job]!!.cancel()
        super.onDestroy()
    }
}

现在,如果我在此范围内启动 CountDownTimer,它不会在 activity 被销毁时被取消。

override fun onCreate(savedInstanceState: Bundle?) {
    ...
    launch {
        startTimer()
    }
}

fun startTimer(count: Long = 1000) {
    object: CountDownTimer(count, 1000) {
        override fun onTick(millisUntilFinished: Long) {}
        override fun onFinish() {
            startTimer()
        }
    }.start()
}

为什么没有取消?以及如何通过取消 activity 作业来具体取消它?

不知道为什么这里要用协程,不过可以去掉,把CountDownTimer的一个实例存到一个变量里,在onDestroy方法里取消:

lateinit var timer: CountDownTimer

override fun onCreate(savedInstanceState: Bundle?) {
    ...
    startTimer()
}

fun startTimer(count: Long = 1000) {
    timer = object: CountDownTimer(count, 1000) {
        override fun onTick(millisUntilFinished: Long) {}
        override fun onFinish() {
            startTimer()
        }
    }.start()
}

override fun onDestroy() {
    timer.cancel()
    super.onDestroy()
}

Why does it not get cancelled?

CountDownTimer 有自己的处理报价的机制,使用 Handler。它不附加到协​​程的上下文。 Coroutine cancellation is cooperative。协程代码必须协作才能取消。如果协程正在计算中并且不检查取消,则无法取消。

There are a couple of approaches 使计算代码可取消:

  1. 第一个是定期调用一个挂起函数来检查取消,例如delay.
  2. 明确检查取消状态,例如isActiveensureActive().