Flow Operators - The Ultimate Guide to Kotlin Flows (Part 2)

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey guys welcome back to part number two of this flows playlist in which i will actually dive deeper into flow operators so what that actually is you will learn that in this video and as usual i will start very simple here and then i will slowly increase the complexity and at the end of this video what we will cover there will be quite complex and difficult to understand but i will actually do my best to explain it as easy as i can so what is actually a flow operator in the end that is just something that decides how oh actually what happens with an emission of a flow so in the recent video we actually built this very simple countdown timer here in form of a flow and we then just collected that so with every single emission with every single second we actually just collect that and print that in our console however there is a lot more we can actually do with flows we cannot only just collect these we can also apply this so-called operators to actually transform our emissions for example is not only to transform emissions but that is one part of flow operators and i will show you the ones that are actually the most used there are some more i won't be able to cover all of the flow operators here but i will show you all of these that i just regularly need in my projects but let's actually switch back to normal collect block here to actually get all emissions and i will also remove the delay here because we don't need this and then let's actually apply a flow operator so very simple one would just be filter you might already know that from lists and that's just useful to filter our flow so we can we now get our time value or emission and we now need to return a boolean expression here if we want to actually filter this out or not so if this is true we will forward it to collect and if not we will just throw it away so we could say if time model 2 is equal to 0 so we actually only want to receive even values that's what that would mean so if we now launch this app and actually go to logcat and we see the current time is 10 8 so now we actually only get even values because we filter these values with this boolean expression so that is one thing you can do with flows which is very very easy and the cool thing is you can chain these operators as much as you like so that will again also just return a flow otherwise you wouldn't be able to call collect on that so we can also take a look at for example map which you also might know from lists already what map will do is well it will actually take the time value which is by the way now the filtered value because we put this filter function in between we now we can now use that to map it to a new value so we could say time times time so we just um yeah whatever value we get in here if it's two we will map it to four if it's three we will map it to nine and then we will collect these values so if we now relaunch this and take a look then we see the current time is 100 64 36 and so on so we now first filter these we only get odd uh we only get even values and then we simply square square these even values and collect them and one more very simple flow operator we have here is actually called on each and that doesn't really transform our values instead it just does something with these so in case you just want to print your values you can do that in here so print line time and you can say yeah time here is pretty much like collect just that you can also chain that with other flow operators so that will return the flow while collect will return nothing so with collect we actually really collect the flow and finish so just make sure that it's finished but on each returns a flow and it won't really finish off the flow here by the way you could also use on each to launch that flow in a little different way without this cruise teens go curtinscope.launch instead we could also just say flow um yeah let's say that on each we print the value and then we could say that launch in view model scope so that would be equivalent to doing countdown flow dot collect inside of a curtain scope here so those are really the few flow operators i need in almost every project those are super often used and those are also very simple like i don't think it's really hard to understand what these do let's actually get to another type of flow operators and those are so-called terminal flow operators so why does why is it called terminal well because these terminate the flow so these basically take the the whole results of a flow all emissions together and then do something with these so let's take a look here instead of collect we could also finish this off with with a so-called terminal operator so we could say for example count which i think is the easiest one so what that will do is it will actually count the values that match a specific condition so if we want to say okay it's let's say model 2 is equal to 0 again so it will first filter these then it will map these print these and then it will take a look if the emission is actually an even number it will count these and it will actually return that as an integer value so val count is equal to this you can see that's an integer and then after that we could simply print this and say the count is count if we now launch this and take a look at here say the count is of course the flow needs to finish first which takes 10 seconds here i think yeah should take 10 seconds and then it says the count is 6. so there are six numbers that were actually divisible by two so that were actually even numbers and of course it now considers that we actually uh filtered and mapped these first so it will take these emissions and not the the direct emissions of our account on flow so if we don't have these then it would look differently again however let's take a look at a different terminal flow operator and you might also already know that from lists which is called reduce and that will also actually return an integer here now in our case because yeah our flow also emits integer values and this on the one hand has a so-called accumulator and a value so we now get two values in here what's the reason for that well reduce let's actually call that reduced result or so reduce will now we executed for every single emission the accumulator is actually it's a little bit hard to explain but let's first actually return something here and then it will be easier to understand that so we could say accumulator plus value and what reduce would now do in this case is it would be executed for the very first two emissions so which would be 10 and 9 here it would add these so it would be 19 for the third emission the accumulator would be 19 and this would be 8 which would be the next emission so it will add 19 and 8 but it would be 27 then for the next emission 27 would be the accumulator it would add that with seven so we would have 34 and so on and so forth so we can actually go through our list and yeah have this accumulator value to basically add all items together for example or to do some other stuff with that so if we actually now also make sure we print that here and i'll actually decrease the starting value to five so we don't need to wait that long every time and we launch this then we can oh it says the count is but it doesn't matter here let's wait five seconds and then it will print the count as 15. because it actually now took all the values from one to five and added these up so this is basically one plus two plus three plus four plus five and that is fifteen and the same as reduced we also have a function called fold which is very similar so we can use fold and all that's different here is actually that we need to provide an initial value so we can say that's 100 and then it will just start at 100 and then add these items from there on so if we relaunch this then wait five seconds and we see okay now it actually took the 100 as a starting value and then added our 15 on top of that so i think this is pretty clear and also not too hard to understand i think okay so far so good for the terminal operators now it's actually getting a little bit more complex when we get to flattening you might also already know that from lists so when we speak of flattening a list that just means if let's say we have a list of lists you just have a list and like it has a list with three elements in it and a list with four elements in it and if we say we flatten this list then we basically remove the lists in it and we just want to have a single list with all the entries um if that makes sense so maybe i'll just write a simple comment here so we might have something like this and one two three so that would be a big list of smaller lists and if we want to flatten these lists if we flatten this list then it would simply look like this so we basically just remove the inner lists and with flows we have something very similar so in flows we don't use we don't really flatten lists instead we flatten flows so if we have two flows then we can kind of combine these to have the results coming in a single flow for that i will actually not use our countdown timer because i think it's actually easier if we use a more simple flow here let's define that here val flow let's call it flow one maybe and all that will do is it will emit one delay the curtain for 500 milliseconds and we emit two so very simple flow and now we actually have a flow two which yeah i don't know maybe emits something something else yeah let's actually not do that here let's create that flow inside of the function that i will show you now um then it will get a little more clear so what we can do is we can say flow one dot plat map and you can see we have three different versions of a flat map here flat map concat flat map latest and flat map merge so all these functions in the end do something very similar well they basically flatten the flow or two flows let's start with flatmap concat so in here you can see we get the integer value this is triggered for every single emission from that flow so here for that one and for that two and now this function if we take a look here um needs to return a flow so here in here we would now create our other flow that would emit something that can be based on the value of the first flow so here we get the value and we could say the second flow now emits the value plus one for example delays for 500 milliseconds and emits the value plus two and then if we say okay uh we collect this maybe get the value in here and we print the line the value is value so let's see actually what does what that does taking a look here in lockhead and say the value is and it prints the values 2 which is well it is the first emission which is 1 and now it will go instead of the second flow and add 1 on top of that so we get 2 as the first emission then the second flow here will actually also emit value plus two which is one plus two so we get three then the first flow will emit again it will emit two and then it will go into this second flow emit two plus one which is three again and then two plus two which is four i know this is very abstract here let's actually think of a more practical use case of that and that would be let's say you have an app a recipe app in which you might cache some recipes so there might be a remote api and you might also have a local database in which you save your recipes and as soon as you actually access these then you get the result as a flow so you might first get the recipes from your database because that's just quick then you make the network call inside of the flow that the yeah that the caching mechanism actually returns and if you actually then get the result from the api then you emit the actually up-to-date recipes that you get from the api so that's how you actually get one recipe however if you now want to get multiple recipes and actually combine these emissions in a single flow then you can use a flat map operator so yeah let's let's try to simulate that here with the recipes i talked about so let's say this would be a flow that just emits some ids for recipes so we could say one two five that has float so that's just the flow that will immediately emit the values one to five and then you could say flow one at flatmap concat and in here you would say get recipe by id and you actually get the id in here so that would be a function that accesses your cache and returns a flow and you just take all these emissions that come from this function and collect them here in a single block but you don't just do that for a single recipe instead you do it for five recipes and you get all these emissions here in a single collector so that is one use case you could actually use this flat flat map concat for we also have a flat map merge operator which is actually less used and if you take a look at the documentation um i think it also says something like it's it's rarely used or should rather not be used um here know that even though this operator looks very familiar we discourage its usage in regular application specific flows blah blah blah so it's not that often used but let's actually talk about the difference to flat map concat while flat map concat will actually finish this flow's emission and then actually will go on with id2 finish id2 emissions id3 and so on while on the other hand flatmap merge will actually do all that at the same time so it won't wait for the first emission to actually finish it will directly launch the flows for id 1 2 3 and so on and then we have flat map latest which is already yeah it sounds a little familiar to collect latest and it actually does the same thing so if there is a flow that like let's say id 2 actually finishes before id 1 then it will simply drop id 1 and only forward the result from id2 to the collect block that will only consider the latest one so far for this flattening operators i know this is quite hard to understand especially if it comes to practical use cases um i hope this made some sense with these recipes here however i want to get to one more set of operators we have for flows and i don't have a specific name for these but these are basically all used to determine what happens with the emissions so how are emissions forwarded for that i actually want to create a flow here and to make that a little bit easier i will think of a real world scenario here like not in terms of coding but in terms of something that you actually will know and that is just ordering something at a restaurant so let's say you're at a restaurant and you first want to order an appetizer then you want to order your main dish and then you want to order a dessert let's try to simulate that in a flow we're going to say okay we first delay that flow by 250 milliseconds and then we say okay we emit the appetizer because that's just the time it took to produce that for the restaurant then we can delay again because now the restaurant is actually preparing the main dish so we could say okay that maybe takes one second to finish after that we emit the main dish and then let's say the dessert is just some ice cream that is already finished so that's pretty quick and we emit the dessert so what we can now do with this is we can say flow on each and we first want to print like that's not the string which is either appetizer maintenance or dessert want to print it is delivered and i actually say okay that comes in a flow so we can just easily easily filter that unlock cat so we just get the info okay now the restaurant actually delivered that specific thing then we can say okay we collect the current dish and we of course also need some time to actually eat that so we can first say we print um now eating it so whatever we got we're now eating that eating takes let's say 1.5 seconds and then we want to have another print line statement after that that says flow finished eating it and if we now actually launch that and make sure we filter that here for flow um clear lock cad and let's see what actually happens if we just leave it like that without any operators so we you see the appetizers delivered now eating appetizer finish eating appetizer main dish is delivered now eating main dish and so on so if we don't do anything extra then we do what pretty much every normal person in the restaurant would do is we eat the appetizer when we finished eating the appetizer we actually ordered the main dish when we finished eating the main dish we ordered the dessert and then simply eat that dessert so what's happening here is all the suspending calls instead of this collect block like delay will also delay the flow here so the flow will only go on with emitting values if our collect block actually finished so if we finish eating our current dish then the flow is ready to emit the next dish however there are other strategies for that strategy number one is actually called buffer so what buffer will do is there's a flow operator which we can simply put in between here what buffer will do is it will actually make sure that this stuff runs in a different queue routine than this stuff so let's actually try this out and see what happens and then i will explain what happens the appetizer is delivered no i can't even read that that quickly you can see it's it just runs through a little bit quicker than before let's go through it one by one first the appetizers delivered that we're eating the appetizer but then the main dish is already delivered even though we haven't even finished eating the appetizer so now this flow will actually go on even if the collector hasn't finished yet so that would basically mean as soon as we get a specific dish so as soon as we get the appetizer we order the main dish and we don't order it as soon as we finish the appetizer instead as soon as we got the appetizer but we will still eat all that in sequence so we just get um we we just get the main dish in the dessert when we haven't even finished eating the appetizer but when we do then we simply go on with the main dish and after that we simply go on with the dessert however there is a different strategy which is number two here and that is called conflate so instead of buffer we can say conflate let's launch that and this is maybe not too obvious on the first glance let's wait until these emissions actually finished here okay we finished eating the dessert let's go through it so first of all as usual the appetizer is delivered we eat the appetizer then the main dish and the dessert are actually delivered together which was exactly the case like with buffer so this is also launched in a separate curating so it's the collector actually collects independently of this flow of of how this flow is emitting and then let's take a look then we finished eating the appetizer and then we're directly going to the dessert so yeah we basically completely skipped the main dish here if we use conflate so what conflate is actually doing and what's the difference to buffer if there are two emissions from the flow that we can't collect yet like here where the main dish is delivered and the dessert is delivered but we're still eating the appetizer we haven't finished eating it yet then when we finish it we will directly go to the latest emission which is the dessert so we will basically drop all emissions that came before the dessert which is in this case just the main dish so as soon as we finish the appetizer we will directly go to the dessert and finish it and we completely skip the main dish and then there is actually also a third operator of these which you already know and that is collect latest so that will really only consider the latest emission and yeah let's try that that instead of using conflate we use collect latest launch that and see how that would behave in this case so we see appetizer delivered eating appetizer main dish is delivered eating main dish dessert delivered eating dessert and finished eating dessert so the difference now is that we actually as as soon as we get the main dish we stop eating the appetizer you see there is no line that says finished eating appetizer and there's also no line that says finish eating main dish because we're just so so happy about the new dish that we directly eat it and throwing away the other one so that is how collect latest behaves in that way very complex and difficult to understand i know however there will be a third video in this playlist in which i will talk about shared flow and state flow which will be easier to understand so definitely don't miss that and as soon as that's out you will be able to find it right here
Info
Channel: Philipp Lackner
Views: 5,159
Rating: undefined out of 5
Keywords: android, tutorial, philip, philipp, filipp, filip, fillip, fillipp, phillipp, phillip, lackener, leckener, leckner, lackner, kotlin, mobile
Id: sk3svS_fzZM
Channel Id: undefined
Length: 25min 25sec (1525 seconds)
Published: Fri Dec 10 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.