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?
CodePudding user response:
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:
- The first one is to periodically invoke a suspending function that checks for cancellation, e.g.
delay
. - Explicitly check the cancellation status, e.g.
isActive
orensureActive()
.