Search code examples
androidkotlin-coroutinesandroid-timer

Cancel CountDownTimer when its activity coroutine job is cancelled


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?


Solution

  • 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().