Cancel CountDownTimer when its activity coroutine job is cancelled

409 views Asked by At

I have coroutine scope assigned to an activity lifecycle like this

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

Now if I launch a CountDownTimer within this scope, it does not get cancelled when activity is destroyed.

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()
}

Why does it not get cancelled? And how to make it get cancelled specifically by cancelling the activity job?

1

There are 1 answers

5
Sergio On BEST ANSWER

I don't know why you use a coroutine here, but you can get rid of it, save an instance of CountDownTimer to a variable and cancel it in onDestroy method:

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 has its own mechanism of handling ticks, using Handler. It is not attached to a coroutine's context. Coroutine cancellation is cooperative. A coroutine code has to cooperate to be cancellable. If a coroutine is working in a computation and does not check for cancellation, then it cannot be cancelled.

There are a couple of approaches to making computation code cancellable:

  1. The first one is to periodically invoke a suspending function that checks for cancellation, e.g. delay.
  2. Explicitly check the cancellation status, e.g. isActive or ensureActive().