As launchWhenStarted and repeatOnLifecycle(STARTED) provide completely different functionality (launchWhenStarted suspends the execution of the coroutine, and repeatOnLifecycle cancels and restarts a new coroutine), if the names of the new APIs were similar (for example, using launchWhenever for the restarting APIs), developers could’ve got confused and even use them interchangeably without noticing.
source
What is a simpler explanation for when to use which?
launchWhenStarted is just a one-time delay.
repeatOnLifecycle creates a suspending point that acts as a handler that runs provided block every time the lifecycle enters provided state and cancels it whenever it falls below it (so for STARTED it happens when it gets stopped).
Update:
For more information: https://medium.com/androiddevelopers/repeatonlifecycle-api-design-story-8670d1a7d333
repeatOnLifecycle restarts its coroutine from scratch on each repeat, and cancels it each time lifecycle falls below the specified state. It’s a natural fit for collecting most flows, because it fully cancels the flow when it’s not needed, which saves resources related to the flow continuing to emit values.
launchWhenX doesn’t cancel the coroutine and restart it. It just postpones when it starts, and pauses execution while below the specified state. They plan to deprecate these functions but I suspect there will need to be some replacement if they do, for the case where you are calling some time consuming suspend function and then want to do something when it’s done, like starting a fragment transaction. Using repeatOnLifecycle for this would result in redoing the time consuming action.
A cold flow backed by a channel or using operators with buffers such as buffer, conflate, flowOn, or shareIn is not safe to collect with some of the existing APIs such as CoroutineScope.launch, Flow<T>.launchIn, or LifecycleCoroutineScope.launchWhenX, unless you manually cancel the Job that started the coroutine when the activity goes to the background. These APIs will keep the underlying flow producer active while emitting items into the buffer in the background, and thus wasting resources.
To solve this issue with these APIs, you’d need to manually cancel collection when the view goes to the background. but it sounds to be a boilerplate code.
That's why Google recommended repeatOnLifecycle(Lifecycle.State.XXX) to make it simple and safe.
repeatOnLifecycle is a suspend function that takes a Lifecycle.State as a parameter which is used to automatically create and launch a new coroutine with the block passed to it when the lifecycle reaches that state, and cancel the ongoing coroutine that’s executing the block when the lifecycle falls below the state.
Learn more from here
Related
I am mastering Kotlin coroutines and trying to figure out
1- what is hot flow and cold flow ?
2- what is the main difference between them?
3- when to use each one?
A cold stream does not start producing values until one starts to collect them. A hot stream on the other hand starts producing values immediately.
I would recommend to read below to understand hot and cold steams with usage:
https://balwindersinghrajput.medium.com/complete-guide-to-livedata-and-flow-answering-why-where-when-and-which-6b31496ba7f3
https://developer.android.com/kotlin/flow/stateflow-and-sharedflow
Form Here (+)
Cold vs Hot streams
Well, I really struggled with this concept because it is a little bit
tricky. The main difference between cold and hot happened to be pretty
simple: Hot streams produce when you don’t care while in cold streams,
if you don’t collect() (or RxJava-s equivalent subscribe()) the stream
won’t be activated at all. So, Flows are what we call cold streams.
Removing the subscriber will not produce data at all, making the Flows
one of the most sophisticated asynchronous stream API ever (in the JVM
world).
Cold flows are created on-demand and emit data when they’re being observed. Hot flows are always active and can emit data regardless of whether or not they’re being observed.
The main difference is that a cold flow is a type of flow that executes the producer block of code on-demand when a new subscriber collects. A hot flow is always active.
See examples of usage in Manuel Vivo's articles from "Google Android developers" group:
A safer way to collect flows from Android UIs
Things to know about Flow’s shareIn and stateIn operators
Cold flow is what you need if the flow needs one collector. It does not get triggered unless there's a collector. Each collector has its own instance of the underlying cold flow. It gets collected and it's done & gone.
Hot flow, on the other hand, does not really care too much about if it's being collected or not at that very moment. It's always around (in memory) till it's GC'ed and emits events from possibly multiple coroutines. If parties are interested in, they can subscribe by calling collect, and all collectors will get the same event emitted from that hot flow. Much like an event bus.
Unlike cold flows, hot flows are always in memory even tho when there's no subscribers/collectors and they're active by default. Cold flows are lazy.. However, hot flows are better for sharing a state across multiple observers.
If you have multiple collectors to share a single state, then you can opt for state flow where a potentially heavy background operation is avoided.
You can expose a cold flow to your UI layer (i.e.: to a view model) and then turn it to a hot flow (for example, using shareIn) which then gives you the benefit of scoping the hot flow to the view model.
Cold Flow:
A flow is called "cold" because every time a terminal operator is called on the flow (ex. collect) it needs to execute the producer code.
Hot Flow:
On the other hand, a "hot" flow doesn't need to execute any code when it's collected because holds the latest state in memory.
Also, cold flows only emit when there's a collector while hot flows don't care about it and always emit.
I develop apps for Android. I was wondering how many Kotlin Stateflows can I observe at one time? Every observe that I do is done on different CoroutineScope created by myself, dispatched by IO dispatcher or provided by lifecycle components of Android frameworks.
I have done various operations such as simple additions in infinite loop inside coroutines and using Android Studio profiler I have observed that launching a lot of coroutines that perform calculations causes high load on CPU.
Having in mind that Stateflow never completes, every collect on it is blocking and done on different CoroutineScope as examples and docs says, what is maximum amount of Stateflows that I can observe at one time without bothering that I will highly use CPU, create too many threads or just simply run out of device resources?
Subscriptions are still coroutines, and coroutines are cheap. There's definitely no universal bound we could tell you.
every collect on it is blocking
It suspends, it doesn't block. And you can always use takeWhile or the like to only collect from it until you can stop, or you can cancel the coroutine that's doing the collection (e.g. with withTimeout).
The main constraint on performance is that updating a MutableStateFlow takes time linear in the number of subscribers to that StateFlow, so if you update a StateFlow with a thousand subscribers, it'll take ~a thousand times longer than updating a MutableStateFlow with only a single subscriber.
There are two similar extension method in androidX lifecycle-ktx package with slightly different signature. withStateAtLeast and whenStateAtLeast. I read the doc but couldn't understand the difference in their behavior. An example on when we should use which one, would be appreciated.
Looking at the source code, whenStateAtLeast runs the given block when the lifecycle is at least in the required state and suspends the block if the lifecycle moves to lesser state while block is running.
At the same time withStateAtLeast just waits for the lifecycle to be at least in the required state and runs the block. So it guarantees that the lifecycle state meets the requirement at the time it starts the block, but if the block suspends, by the time it's resumed, the lifecycle can be in lesser state or even destroyed.
So in general withStateAtLeast is a good choice if you need to run a block when lifecycle reaches the state (e.g. user entered the screen) and finish its execution in any case, even if the user leaves the screen before the execution finished. whenStateAtLeast in its turn is useful when the block works with UI, it guarantees, that each time it resumes execution, the lifecycle is in proper state, so you can access UI safely.
I am trying to implement a UI in a fragment, where user can make all sorts of updates and I need send it over to backend when user EXITS the screen. (Batch update)
I am using MVVM pattern, where network calls are performed from viewmodel . Now, viewModelScope.launch won't work here, since as soon as user exits, the coroutine is canceled by onCleared().
For now, I added GlobalScope and it works but I have also come across this and this question
Are there any other alternatives to accomplish this with Coroutines?
Coroutines are mostly recommended for work that should start immediately and is scoped to the lifecycle of a Fragment, Activity, ViewModel or any other object with a lifecycle. Since the rest of the coroutine builders are tied to scopes, they wont accomplish what you are trying to do, since the user might leave your app at any given time.
A better approach would be using WorkManager with CoroutineWorker, which isn't tied to your UIs or App lifespan and still takes the advantages of working with Coroutines. With WorkManager, your work could be enqueued when the user leaves your designated screen and will be guaranteed to run once the constraints you specify are fulfilled (for example having internet connection). I recommend you to check Android's Guide to background processing if you are still making up your mind on which solution to use.
There are several situations in my codebase where a stream I'm subscribing to will only ever emit one result and as such it makes sense to use rx.Single rather than rx.Observable. The documentation for Single says the following:
A Single will call only one of these methods, and will only call it
once. Upon calling either method, the Single terminates and the
subscription to it ends.
With a traditional Observable I capture a reference to the Subscription so that I can unsubscribe at an appropriate time and not cause memory leaks:
Subscription s = getObservable().subscribe(...);
subscriptions.add(s);
subscriptions.clear();
My question is whether this is necessary with a Single or whether due to the fact that the subscription ends immediately it could be left simply as:
getSingle.subscribe(...);
Without any negative repercussions of references being held onto into the subscriber.
Single doesn't tell you anything about how long it will be running.
Since you're targeting Android, the answer is yes, you should keep the subscription and unsubscribe.
Imagine you're switching Fragments/Activities and a long running SingleSubscribers's onSuccess is called. So the best time and space is probably in onPause(), but it depends on your context.
You might run into NullPinterExceptions, Adapters being filled multiple times or similar problems if you don't unsubscribe.
Any reason why you need to cleanup the subscription on Observable?. Observable by design once that the observer has all items is automatically unsubscribed.
And then, since the instance is not reference anymore the GC at some point will clean up for you.
You can see in these example how the subscription is unsubscribe after onComplete is reach.
https://github.com/politrons/reactive/blob/master/src/test/java/rx/observables/creating/ObservableSubscription.java