You're Collecting Your Flows Wrong In Compose | THIS Is Why

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey guys and welcome back to a new video in this video I will show you why it is a mistake to collect your flows like this in Japan compose with this collect as state function I mean I could just go ahead and tell you the solution what you should do you should use collect our state with a life cycle but I'm a big fan of not just using things but also understanding why you should use them this new collector state with lifecycle API is now considered stable which is why I also start using it but let's first of all dive a little bit into how flow collection for compose works and why this older approach is actually an issue in Android so the little sample app I have created here is just a normal compose product with two screens screen one just consists of a timer so or rather just a number that counts up that time is then shown on the screen just using a text and when we then click on a button on that screen we get to the next screen this is just a very basic setup that can show us when flow collection actually starts and compose when it stops and what the problems with our approach can be taking a look in our review model we see that we just have our current time which we simply emit here in our flow so in the flow we just run a while true loop as long as the flow is being collected and every second we just emit the new current time and we also print that the flow is still active so that we can also see if the collection still happens in certain scenarios we then call Dot state in which is just the normal flow operator to convert normal flow or any type of flow to a state flow which will on the one hand catch the latest value so it will cache the last emission of this flow so the current time in this case and therefore also allow multiple observers to collect this flow at the same time with a given sharing strategy which is this one here so that all these observers get the same emissions at the same time the sharing starter lazily strategy just means that this flow will start executing as soon as the first subscriber or the first collector appears and then it will never stop at least it will never stop until the curtain scope in which this flow is launched is canceled so in this case until view models jobs canceled now let's say we just try this out and see what the issue with this approach is if we also collect it with collect as state I will launch this on my device here take a look here and you can see this is just the screen we have and every second it will count up here in our console we get flow is active every single second so we know that the collection is still executed let's also switch to lockhead so we see this if the whole window is flooded with these logs and if we're done go to screen number two we will see something interesting we're now in screen two but you can see in lockout that we still get a new log every single second and usually that's something you don't want for flow collector that is only relevant for updating the UI usually as soon as you would go to this next screen you would want the flow to stop collecting but if we now go back we see that it's still counting up and it also counted up while we were on the next screen that is something we usually don't want let's take a look how we can fix this with our approach by going into our screen one view model if you've listed carefully then you know that I sat at this lazily strategy just launches this flow and executes this time on this case as soon as the first collector appears and then never stops until the scope is actually canceled and you might probably know that an Android The View model scope is canceled as soon as we pop the corresponding screen this view model is bound to from the back stack however if we navigate through new screen the old screen is still on the back stack so the view model is also not cleared and therefore this flow not canceled what we can do in this case is we can replace this sharing strategy with while subscribed this will only keep the flow active as long as there is at least one subscriber or one collector and since we use collect as state which serves as such a collector which is a composable function so it knows about our current composition and when when it's not active on the screen anymore this will also tell our flow when this subscriber disappears when we navigate to the next screen so if we now relaunch this take a look look in our lockhead take a look here we can see that it's coming up we can see the logs but if we now go to screen number two we see that we don't get any logs anymore because now the flow is basically canceled since there is no active collector anymore so does that mean we already solved our problem without even using the new API no it's sadly not that easy let's go through one issue this approach actually brings if we now go back we see that the flow is still at its old value but what happens if we rotate our device one issue is that if we have screen rotation here then our activity will effectively be recreated so that means it will go through its lifecycle up to on Destroy so the activity will be destroyed and then simply on create will start again and that means there will be a time window in which there is no no active subscriber which is for example in the on Destroy State and that also means that in this little period of time this flow will not be collected and therefore the emission will stop and as soon as our activity is recreated and on create starts again a subscriber will appear again and that will also start the flow emission again so in this case this would lead to inconsistent timer results because for example if we rotate our activity when we already delayed for 800 milliseconds then this flow would be collected again and we would effectively wait 1.8 seconds for the next emission and this is really just an example don't build your timers like this with just having a delay function so one problem of this approach is that if the activity is recreated for example when we wrote it our screen then the flow is collected from the beginning again which we usually don't want so to solve this you will often see something on the Internet also recommended by Google to pass in a parameter here a stop time on milliseconds and usually you see the value of 5 Seconds so 5 000 milliseconds what this will do is that after what this will do is that after the last collector of this flow disappears The Flow will still be um executed for five more seconds so this will be enough time to let the where there is no collector of this flow so to solve this you will often see something on the Internet also recommended by Google to pass in a parameter here a stop timeout milliseconds and usually you see the value of five seconds so 5 000 milliseconds what this will do is that after the last collector of this flow disappears The Flow will still be executed for five more seconds so this will be enough time to let the activity fully rotate and recreate and then it will subscribe to this flow again and since we have the stop time on milliseconds the flow won't be interrupted so this approach it will just work fine to rotate our phone when the timer is running without the flow being interrupted so if we rotate this here then we will see the emissions are just coming in perfectly fine so now we basically use this approach which is recommended by Google but why do we then have this API which I teased in the beginning so if we take a look here we are still using collect as state but I recommended to use collect as state with a life cycle what does this API do what collect a status not do let's take a look at collect a state and now I will finally show you the issue with this approach if we run our app and we take a look here in our logs and we put our app in the background just minimize it you can see the flow collection is still active and that is something you usually don't want for flows that are just relevant for UI updates and the reason for that is that this collector State function has no information about the current life cycle state of our activity or fragment so while our UI does not update itself using the flow's results the flow is still being collected so this this piece of code here is still going to be executed when the app is in the background and it of course depends on the flow if you want that behavior or not well in this case if you want the timer to be active in the background you would rather want to use something like a foreground service or so so let's now go to main activity replace this with collect as state with lifecycle and for that you by the way need a new dependency which is this one here so the lifecycle runtime compose dependency with this version because in this version this is stable before you need it's an experimental annotation which is why I did not use that in my previous videos but now it's totally fine to use that and if we now finally relaunch our app take a look here take a look at our logs and we minimize the app then for five more seconds since we use this while subscribed with five seconds we get logs but after that the logs disappear so this combination is really the officially recommended approach from Google right now that you use collect as stable lifecycle in combination with this while subscribed in five seconds and it's probably worth mentioning that you don't need all this extra setup here for the normal uh stay flows which you often see is something like private valve counter is mutable State flow zero and then you have a public exposed version which is just counter.s stateflow with this app State flow operator which you can only apply to normal State flows and not to normal flows like this here this is not a problem at all because there is no underlying flow that performs specific actions or that has some kind of reactivity you only need to consider what I mentioned here so in this case you should still use the collect estate with lifecycle API but you don't need to use the state and operator for all of your state flows but rather only if you want to convert a normal flow to a state flow and finally you might wonder why is that not the default why is there collect a state which is wrong and then there isn't a separate collect a stateable lifecycle API which is the one we should always use and the reason is that you should not think about compose as Android specific compose can also be used for desktop or they plan to make it work for web and they even plan to make it work for IOS and Android is the only platform which has such a specific lifecycle maybe iOS has that as well I'm not sure but an Android we really have this typical activity and fragment lifecycle so this problem of having this difficult collection logic is only a problem specific to the Android domain so you can consider this correct as state I'll collect our state function as the normal function to just collect the flow in a composable but this one is the Android specific API which you should use in Android project so I know this video was in the end just swapping out the collect estate function with this new function and talking a bit about these sharing strategies here but I think it's quite important to understand how flows work under the hood because you really don't want to blindly use new functions or new apis in your project without understanding why and what they actually do so I hope this video helped you to understand that and if so you will definitely also love my more advanced Android premium courses which you can find using the first link in this video's description and apart from that I wish you an amazing rest of your week I will see you back in the next video bye bye thank you foreign
Info
Channel: Philipp Lackner
Views: 26,886
Rating: undefined out of 5
Keywords:
Id: 0G_1XCRlpYM
Channel Id: undefined
Length: 11min 34sec (694 seconds)
Published: Sun Apr 16 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.