3

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 observedAsState in a @Composable

lineage
  • 792
  • 1
  • 8
  • 20
  • *wastes resources as a thread should exit after performing its task instead of going to sleep* - could you please explain what you mean? Also, which thread are you talking about here? The main thread from the `viewModelScope`? How is this using more resources than `CountDownTimer`? – Joffrey Jan 06 '22 at 21:47
  • @Joffrey I meant the thread that executes the coroutine...I have no idea about the internals of how couroutines are implemented but I guess the `launch` actually launches a thread that keeps getting `suspend`ed as it encounters `delay`. I didn't mean the Main thread but I may be talking nonsense here – lineage Jan 06 '22 at 21:53
  • @Joffrey regarding the `delay` wasting resources, the hesitation stems from the general frowning in `Java` for `Thread.sleep` to which the former seems similar to me – lineage Jan 06 '22 at 21:57
  • 2
    `launch` doesn't start a thread on its own, it would defeat the purpose of coroutines. How coroutines are run depends on the dispatcher used to launch them. In general they are dispatched on a thread pool. In this case the `viewModelScope` defines a dispatcher that uses the main thread to run coroutines. So your coroutine is effectively run on the main thread – Joffrey Jan 07 '22 at 09:50
  • 1
    As you may have guessed, unlike `Thread.sleep`, `delay()` doesn't block the thread (blocking the main thread would be really bad, and you would have noticed). Instead it just suspends the current coroutine, which means it frees the thread so that it can go run other coroutines. – Joffrey Jan 07 '22 at 09:52
  • never mind got it .....https://stackoverflow.com/q/62166878/10115137 – lineage Jan 07 '22 at 13:31

1 Answers1

6

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.

Tenfour04
  • 83,111
  • 11
  • 94
  • 154
  • 1
    *In a coroutine, delay does not lock up any threads or coroutines, so the same concerns do not apply.* that cleared things up thnx – lineage Jan 06 '22 at 22:02