As per the commentary of this answer, using CountDownTimer
val timer=object:CountDownTimer(Long.MAX_VALUE,10){
override fun onTick(p0: Long) {
_mutableLiveData.postValue(newValue)
}
override fun onFinish() {
TODO("Not yet implemented")
}
}.also { it.start() }
from inside a ViewModel
or otherwise would cause memory leaks. On the other hand implementing a timer using viewModelScope.launch
viewModelScope.launch {
while (true){
_mutableLiveData.postValue(newValue)
delay(10)
}
}
from inside the same ViewModel
wastes resources as a thread should exit after performing its task instead of going to sleep.
Which way should I use?
Is there some other idiomatic way that I am missing out?
The context of my question is this: in a ViewModel
, my timer implementation (currently using delay
) periodically changes the state of a private MutableLiveData
that is being observe
dAsState
in a @Composable
A CountDownTimer only leaks memory if you don't clean it up when its associated objects are passing out of scope. So in a ViewModel, you should store a reference to it in a property and call cancel()
on it in onCleared()
. In an Activity, you'd cancel it in onDestroy()
. And so on.
The viewModelScope
is set up to automatically cancel any coroutines it's running when the ViewModel is destroyed, so you don't have to worry about leaks.
Likewise, lifecycleScope
in an Activity or Fragment do the same, and viewLifecycle.lifecycleScope
does it for the View life of a Fragment.
Calling sleep
in a thread prevents that thread from doing any work, so it's hogging a core of the processor. And it must never be done on the Main thread because that would lock the UI.
In a coroutine, delay
does not lock up any threads or coroutines, so the same concerns do not apply.