Android Build Powerful Flow Chains in Kotlin (flowOn, collect, map, ..) - Android Studio Tutorial

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
what's up guys and welcome to this video about kotlin purity laws in this video we are going to build an own float chain where we emit values and apply a lot of operators on it also do context switching from the data layer over the domain layer to the presentation layer and then we finally collect the values and can show the user a corresponding percentage value because we will simulate some heavy data processing and always update the UI how much process we actually have now let me quickly show you this in this example app we will click on start export and then you can see that there's a heavy processing task in the background and we get the corresponding percentage value so that our UI is really responsive all right okay I'm in an empty jetpack compose project in case you're using XML for your layouts this shouldn't be a problem since we only have a simple button and a simple dialog which shows the current percentage the rest of the logic will be in our data layer and in our view model the first thing we want to do is to build our flow actually we want to have a route where all the values get emitted where we can then apply different operators until we collect all those values for that we will go to our root package and create another package called Data because we will do this in the data layer we will simulate some heavy data processing which normally happens in the data layer and we will create another package inside the data layer called converter because in this case we have some converting data logic which we will consider as our long processing task here in our converter package we can create a kotlin class called user data converter and normally in terms of clean architecture I would put this behind an interface and then do some dependency injection with dagger hild but I think for the sake of this tutorial it's fine to use a plain glass in here we will have a functioner convert user data this will be our flow Builder function this takes a user data list of type load this is not sure to specify it just some user data here and it will return a flow of type data converting info this is a data class we will Define after we apply this flow block here and as you can see we can now create a flow this all those functions here return a flow and we will use this flow Builder because there we can emit multiple values we could also just use this flow of for example and provide a value like this but this would only provide the flow of one single value and if you use this flow Builder we can let's remove these errors and then in here we can just say Emit and emit again and for for Loop for example which we will have here we will emit values over time let's create this data converting input data class down here normally I would also put this in a separate file but I think for this tutorial this is also fine this will contain a value converts its data amount of type integer and this will indicate how much data we actually converted and depending on that we can apply a formula to calculate the current percentage and let's also import a flow to get rid of this compiler error and then we will start with a bar already converted values which is equal to 0 by default and then we will say userdata dot for each and in here we don't need actually the user data because we will just simulate some heavy processing we will say 4i in 0 until 1000 we will have a value of J is equal to I dot 2 Double Power of 2 for example just some heavy data logic later on we will pass a list with 1 million entries and then this will take a while like 5 or 10 seconds or something like this and for each iteration in this for each block be careful not in this four Block in the for each block for our user data we will emit a value which indicates how much we actually converted already let's say emits data converting info converted data amount is equal to plus plus already converted value this will increase the already converted values each iteration in this for each Loop by one and will also emit this value so that we are always up to date how much values are actually converted and then we can apply different calculations to get the current percentage and to show the user the current progress let's go to our repository class now which will also have a functioner and subscribes to this convert user data flow Builder function and applies different mapping operations like the percentage calculation which gets then provided to our viewmodel for the UI in our data package we will create another class called user repository and again I will just make this a class in a real world application of course we should make a repository class an interface in the domain layer and then confirm to this interface in the data layer or realize this interface in the data layer in terms of clean architecture we will have a functional generates user data this will also take user data list of floats which is not further specified and this will in this case return a flow of data progress info which is a data class which we don't have yet and the reason why this function is called generate user data and our converter function was called convert user data is because maybe in a real world application you would have some raw data and you first need to convert them to get the generated data and then you can save them in a file or something like this so the repository is responsible for generating the user data but uses the user data converter for converting the user data this is where the name comes from let's create our data class for the data progress info and this will take a value progress percentage which will go from 0 to 100 later and of course we could also make this flow return an integer or emits integer values but maybe you also want to provide additional information then this should be of course a data class let's also import flow and then we can open this function block and first of all we need a value values for one percent and this will be equal to userdata dot size divided by 100 so if this user data list has 1 million entries then this would be divided by 100 and the result would be 10 000 and we would need to process 10 000 values to actually get one percent done then we need access to our user data converter which we can get from The Constructor private well user data converter of type user data converter is equal to user data converter so we will simply create the object in the Constructor not the best practice of course you should use some kind of dependency injection framework for that and also use an interfaces but as I already said this is fine for now in here we will say return userdataconverter dot convert user data we will pass the user data list and first of all we want to filter values because now we have a flow returned from here and we can apply different operators on this flow and first we will start with the filter operator if we have a closer look at this filter functioner we can see that we can invoke it on a flow of Type e in our case the type T is data converting info and then we need to provide a predicate so some comparison which return either true or false and then this filter functioner will again return a flow of the same type T so it will just filter out everything that does not match this given predicates and for our case we will have a check on the converted data amount we will say data converting info so that we don't need to invoke it with it we will say data converting info dot converted data amount modulo values for one percent is equal to zero in case you don't know what this modulo operator does it just checks if this converted data amount is dividable by this values for one percent so let's take a quick example we have a converted data amount for example 50 000 then our values for one percent would be for example ten thousands and it would would result in zero if this converter data amount would be 15 000 so it's not dividable by ten thousand then this modulo operator would result in five thousand and this would return false and so the value gets filtered out and we still have a problem because we have a required flow of data progress info but currently this filter functioner just Returns the same flow that it actually gets of type data converting info so we need to do some mapping and for that we can apply the next flow operator which is called map let's open this block and as you can see we again get the access to our data converting info like this and let's check on the documentation for map as you can see here we can invoke map on any flow with type T and in our case type T again is data converting info and then we need to provide a function which Maps our type T to another Type R so we can change the data class or the type of our flow because it will return a flow of time R then and for our case we will say data progress info and we will pass for the progress in percentage our data converting info dot converted data amount divided by values for one percent because we can make sure we can be sure that this is dividable because we already checked it in our filter operation here and then the error is gone and we successfully mapped a data converting info object to a data progress info object which now holds the current percentage of our progress in case you want to do more mapping then you can just apply the map operator again and again and also again the filter operator on each or something like this because all those operators have the same principle they take a flow and do some action or some mapping some filtering on it and then they emit a flow again so the possibilities are endless here and we can build really beautiful blow chains the last operator I want to apply is the flow on operator which takes Security in context which we will pass the dispatchers.io context let's check on the function definition we have a flow of type T again where we can invoke this flow on and then it also Returns the flow of type T so nothing changes here it just takes Security in context and we don't do any processing on the values and let's read this first sentence here it changes the context where this flow is executed to the given context so that means that this flow is executed in the context of this patches dot IO because we will do a heavy data calculations here and this should be done in dispatchers.io creating context and be careful this does not mean if you for example apply some other operators that this year is executed in the dispatchers.io context you need to have a look at this what's kind of below this flow on operator so all this stuff here is executed in the dispatchers.io context and also this year where the flow actually gets created so everything what's below this flow on context switch all right okay let's go to our root package and Define our viewmodel where we collect our flow now we will call this user viewmodel this will inherit from viewmodel and you can also pass the argument in the Constructor private well user repository we will straight initialize it here like we did in the user repository itself with the data converter in the view model we will define state bars for our UI the first Statewide will be our progress state by mutable state of 0f by default we will make this a private set so that we can only change this value inside our viewmodel then we need to import this get and set values for the mutable state of delegate and we will have a while is loading by a mutable state of false and we will make this also a private set and then we initialize this viewmodel with some user data we will say privates well user data is equal to mutable lists of float and this user data list is empty by default in the init block of this viewmodeler we will say 4i in 0 until 1 million we will say userdata dot adds I multiplied with 2f for example just some simple example user data then we can say function start generate this function can be invoked from our UI later this is why it's public we will say is loading is equal to true and now we can collect all the flow values but let me quickly show you something first we will say user repository.generate user data we will pass our user data mutable list and then we could apply for example the on each operator and in here we can say progress state is equal to its Dodge progress percentage for example and I think we have type error okay let's change this progress states to integer and if we let it stay like this then nothing would happen we wouldn't collect anything but you could assume that you get all the values in this on each block because all the values get emitted this is true but only if you also collect this flow this on each is just an operator it does not collect the flow collecting a flow means making it hot because by default a flow is cold so a flow doesn't do anything until it's collected and right now it's not collected if we say collect now then the flow would be collected and becomes hot and collects uh and emits all the values but for this collect functioner we need a creatine scope because this is of course a suspend function and for that we will say viewmodelscope.launch and now we can copy and paste this here and then the arrows gone and this would of course work we would set the progress state to the current progress percentage but we shouldn't use on each for that we can do this in this collect block on each small like an operator where you can do additional action now because it just passes the emitted values through and for each value you could apply an additional action and we should set the progress State inside the collect operator so let's just remove this on each block and for the sake of additional operators we will also apply to map operators here so that you can also see that we can apply them in the view model in the presentation layer and in here we will say data progress info for for example and we will say it's dot progress percentage multiplied with 10 and then we will map it again we will set say it's dot progress percentage divided by 10 and then we get an integer provided here in this collect block and then we can set our progress date of course this is nonsense this two map operators but you can see that we can also apply them here because this whole flow is a large chain and it isn't limited to one single file to one single class it's Limitless until it's finally collected all right okay and the last thing that's left is building our UI let's go to our main activity and first create a private well view model by view models of type use of your model in here we can remove this surface and also this composable preview examples we will have a box which takes a modifier and let's import this box here this this modifier will fill the max size the content alignment will be alignment.center then we can open the box scope and we will just have one partner with an on click method and in this on click method we will invoke the viewmodeler dot start generate and the text of this partner will be start exports and the last thing that's left is our dialog we will go below this box and say if viewmodel dot is loading then we will invoke a dialog or the on dismiss requests and I think we need to import dialog first they're on dismiss request we just pass nothing and then we will have a single column where our progress indicator and the corresponding percentage text is and for the modifier we will say modifier.filmx with the vertical Arrangement will be Arrangement dot space type 15dp the horizontal alignment will be alignment Dot Center horizontally import DP then we can open the column scope and in this column we will have a circular progress indicator the color will be set to color that whites then we will have the text and for this text composables text we will say processing user data and in here we will say viewmodel dot progress States and then we will close the parenthesis and apply a percentage sign and three dots like this and the color will be color dot whites again and the style will be material beam dot typography dot body one and I think then we are going to go to launch this all right okay let's click on start export and then you can see the percentage value gets increased over time because our blow always emits the current processed values and this does not dismiss after it's finished let's go to our view model and in here we will set this loading is equal to false and also the progress States is equal to zero again and then we can try this again and this should be dismissed after 100 percent maybe you want to indicate some CS file processing which needs to be converted to get ready for export or something like this and yeah now this um works very well one last thing that I want to mention is regarding the different execution contexts in our repository we had this flow on operator where we switched the context to dispatchers.io so everything that's below this flow on operator gets executed in this patches dot IO in our view model we don't have this flow on operator because here we launched this in the viewmodel scope so in the curating scope and the viewmodel scope belongs to dispatchers.main so everything that's executed here happens in dispatchers dot main context all right okay this was it for this video thank you very much for watching I hope you enjoyed it and you'll learn something about curating flows different operators in case you want to learn more about other operators or also how to define an own operator then let me know this in the comments and then maybe I will do a video about it
Info
Channel: K Apps
Views: 1,010
Rating: undefined out of 5
Keywords: Android, AndroidStudio, Kotlin, Coroutines, Flows, Android Flow, Kotlin Flow, Kotlin Flows, Jetpack Compose, map, filter, onEach, flowOn, collect, Android Studio Flow, Android Flows, Clean Architecture, Android Context Switching, Flows Context Switching, Jetpack Compose Flows, Jetpack Compose Flow, Android Coroutines, Android Studio Coroutines
Id: _MH95CkzzqA
Channel Id: undefined
Length: 21min 17sec (1277 seconds)
Published: Mon Dec 05 2022
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.