Flow is fun to play around with.
In short, it represents a cold stream of values. Kind of like my typical afternoon at the pub drinking a stream of too many cold ones.
Aside from looking at memes all day we might as well write some code.
FlowGuy seems like someone trustworthy that gets the job done. He holds this stream that will emit a new list containing humans every 1 second.
All this work will happen on the IO thread/scheduler/whatever, just to be safe and keep the UI thread as free as possible.
Since this is a cold stream, nothing is actually happening when we run our app until someone starts collecting this
Am I flowing yet?
This seems fairly standard but the editor will complain 👺:
Makes sense! This flow could take a while and it would be awful to have a memory leak. Let’s use the classic
shameless plug to the best way of (un)learning coroutines:
Since this flow is returning a list of humans, it would be nice to see the results of this happening in real-time with a nice adapter-recyclerview combo.
A fragment with a button and said recyclerview should do it.
- Get an adapter-recycler ready then observe for changes in the livedata variable that the ViewModel holds.
Once a change has been observed we pass the whole thing into the adapter (no fancy ListAdapter, truly cutting-edge stuff going on here)
- A button will trigger the start of the flow. Innocent enough, right? 🤠
Let’s try this out!
That’s it. You are free to go watch YouTube now.
There is something very fishy with this example.
A flow can take milliseconds to complete, or it can take minutes/hours as in the example above. Maybe it will never “really” complete. Room, for instance, provides observable reads with
Flow enabling you to get notified of changes in your database.
Launching a collection from the click of a button, or any 1-shot operation that can be repeated, suddenly doesn’t sound like a great idea.
Case in point.
collect multiple times will not stop the previous collections that are in progress. This is fairly obvious and common knowledge for coroutines being launched for other, non-flow things as well.
Everyone loves working on these mythical projects where code is clean and Uncle Bob is smiling from a distance being proud of you.
In truth you get massive codebases, files with thousands of lines and god knows what else. It’s easy to get lost. Even obvious stuff like this gets into production code. Normal users have a habit of rotating the device and spamming buttons a lot, which leads to unexpected behavior you wouldn’t think possible.
It’s also not too uncommon to see suspend functions being repeated in succession. That rarely causes problems, and it can actually go unnoticed very easily with nothing actually going wrong in the end.
Flow is not as forgiving.
Returning to the original issue- it is fixed easily by keeping a reference to the coroutine/Job and cancelling it before running a collection.
In the event you are interested in only collecting a few values from a stream then there’s a handy operator for that-
Source code can be found here, by the way.
If you found this blog post useful why not give it a clap? It doesn’t mean anything really but my mom would be proud.