MVVM / MVI Repository Pattern with Hilt

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
in this video i'm going to set up a really basic example where you use mvvm or mvi repository pattern and use dependency injection with hilt so what we're going to be doing is we're going to be setting up some really basic architecture like i said we'll have a single activity we're going to have a view model i'm going to show you how hilt makes dependency injection easier with view models it's way simpler than it was before uh we're gonna have a repository and we're gonna have two data sources one will be the cache and one will be retrofit so making a network request so what we're actually gonna do is we're gonna pull some network data from one of my websites it's a public website i got some like fake data up there and we're just going to pull that data we're going to cache it into the sqlite database using the room persistence library and then we're going to display that data on the ui before we start let's go take a look at a demo all right so here i have the emulator on the screen i'm going to launch the application and what you're going to see here is you're going to see a progress bar getting that network data and once it gets that network data it caches it and then displays it on the screen so it's a very simple application just gets some data from the network caches it and displays it the data is coming from one of my websites openapi.xyz you can go to placeholder slash blogs and here's just some json data and this is the data that we're actually pulling to display in the application it's just static data nothing special just something that i i like to have up here just in case i have to you know get some data and you know try and see if something works in an application just for clarification if you want to know where to get the source code for this project the starting point that i'm going to be starting at is this basic mvi repository pattern start branch right here so if you go to the repository uh dagger hilt playground i actually named it player ground that was by accident should be playground this is the starting point code and this is the code that you'll end up with so basic mvi repository pattern just in case you are not familiar with git most most of you probably are if you want to find the branch just click on branch here and you'll find you know the starting branch and then the finishing branch here so if you want to take a look at the code ahead of time go ahead but i'm going to be writing everything out on video so if you want to clone the repository just go to code and you can copy this link and just open android studio which i will do right now and you can go to new imports project conversion control click get and just paste the link right there and click clone if you just want to you know start your own code which which is totally fine because i'm starting with you know pretty much absolutely a blank project you can just you know go to file new new project and create a blank project and you'll be looking at pretty much what i'm looking at here right now i just have you know a single activity i have some some uh stuff in the layout here if i go to activity main i just got a progress bar with a text view you know nothing special here you could easily copy this in and i have some some stuff in the build.gradle file which we're going to go over right now let's close these and we'll go to the to the build.gradle project file first so that's going to be this file right here and again um i have i've gone over kind of all this hilt setup stuff in previous videos i'll put a link up in the corner here to where you need to go if you need to know how to get started with hilt which is the dependency injection framework that we're using but i go over everything here like getting the class path for hilt in the build.gradle project file and if you go to the build.gradle app file we have some more dependencies here all for hilt also so these dependencies right here again there's a link up above there should be a card showing on the video you can click that and figure out how to get started with hilt so got the dependencies got to set the java version right here and make sure to get calling capped get the hilt android plugin which is this one right here and these plugins should come with a new project when you open one with android studio so that's going to be it that's the setup and again you can go watch videos on how to get started with that setup but let's uh let's get started with this project so first things first is we need a way to model the data so i'm actually going to go to open api dot xyz placeholder blogs so this is the data that we're modeling if you want to get a more clean kind of look at what this data looks like we can go to like you know json beautifier butte if fire and just do any of these these are all fine and just paste the json data into here and go process so it's going to give you a cleaner look at what that data looks like so you can see there's a primary key a title a body an image and a category these are all strings except for the primary key which is an integer so that that's what this data structure looks like when it's kind of cleaned up a little bit so essentially here we the first thing we need to do is build a model for this data so like this is a blog object this is a blog object and we need to model that in android studio now keep in mind in the actual application all i'm displaying is the title of these blog posts so if you look here you know here's ready for a walk there's ready for a walk this one says mazie sleeping there's maisie sleeping so i'm actually just using the titles but we're going to get all of the data using retrofit we're not just going to get the title so i want to you know accurately model this data so let's go to android studio and let's build that model so coming on over here to the main package directory i'm going to click on a new package and call it model now inside of model i'm going to build a new model this is going to be the blog class so writing in blog and then selecting class and i can add this to get it doesn't really matter so this is going to be a data class because it's going to structure some some data so i want to get five fields in here variable id id which is going to be an integer we have a variable title which is going to be a string and i'm going to copy that three more times because these are all also going to be strings we're going to have the body we're going to have the image and we are going to have the category so those are all the fields for our data structure i can get rid of these brackets so again just just to kind of remind you let me go to the beautifier again remember we have primary key title body image and category and those are the fields that you see here one question you might have here is why i'm calling this id but in the data structure here it's this is pk so it's primary key why am i naming this different so just just kind of know that this is not a mistake and we're going to be addressing this later using a mapper class so just kind of know that yes i know this is not primary key but we're going to be handling that later now we're going to be working on the network request code everything needed to to make those network requests using the third-party library retrofit so we need to get the dependencies for retrofit before we can we can actually use it so i'm going to open up a new browser here go to you know retro fit you go retrofit two there's the link to the open source library and if you go down to the download section it shows you how to get started using retrofit so here is the dependency i'm just going to copy that go back to android studio go into the build.gradle project file and i'm going to paste this down below and i'm going to clean this up a little bit so i'm going to define a retrofit version which is so i can just say you know retrofit equals 2.2.0 i believe is the newest version if you're wondering where i got that version from you can go to the documentation and it will tell you where to get it so if you click this available on github link that will take you to like the source code and you can see what the newest version is so that's where i'm getting that from i actually think it's not 2.2.0 i think it's 2.6.0 so there's the version now let's pass this as a variable so i'm going to delete these single quotes and put in double quotes and that's going to allow me to pass a variable here so there's that retrofit version we also need one more retrofit dependency and that's going to be for the json converter so if you go to the retrofit documentation and you scroll around here i can't remember exactly where it is here it is there's all these different converters for getting different types of data we're going to be working with json data so the network data is json data so we need to use this json converter right here so i'm just going to copy this com square up retrofit 2 converter json now go back to android studio paste that in and then pass the version the retrofit version here again so retro fit so once we got those we can sync it and android studio will hopefully not give us any errors now we can start working on the retrofit code for the project so i'm going to right click on the main package directory go to create a new package and just call this retrofit typically you know you know you probably don't want to name this retrofit but i'm i'm really i'm trying to simplify this architecture just so you can kind of see the big picture here usually you put it into like if you're using clean architecture you would uh do different uh data sources like a cache data source the network data source but we're just like keeping this really simple here so i'm just going to call it retrofit so it's really clear that you know all the retrofit stuff is going to be inside of this package okay so let's close these up and i'll leave this open because we're going to reference this so the first class that i want to build inside of our new retrofit package is going to be a blog network entity and this is going to be the class that models what the data is going to look like coming from the network so i could call this a data class actually and let's open this up and add some fields so i'm going to use the serialized seer serialized name annotation and get that import this is now the name that i'm looking for in the data so i'm passing pk here and if we go to the whoops i open the wrong window to our data structure that's that's what this needs to match it needs to match the name of the the key of the data that you're retrieving so we're going to have primary key title body image and category all of those uh all those fields then at expose so getting that import and then var id is integer so that's going to be our very first field this is the primary key field so i'm going to copy this a bunch more times should be four more times we have the title is the next one and this is going to be a string and this of course needs to match that because you need to tell retrofit what to look for when it's retrieving that data the next one is going to be whoops the body and that's also going to be a string this will be body next one will be the category and then just change that to category change this to string and the last one will be the image i think the image was actually before the category but the order does not matter here you can do these in any order so just once again these are now matching the the keys for the data that we're retrieving so primary key that's an integer title that's a string body that's a string images string category string so now that we have our data modeled we can build the retrofit interface that's going to be responsible for constructing our retrofit object that gets the network data so if you're familiar with retrofit this is going to look you know very familiar to you you've done this probably 100 times this is going to be an interface and i'm just going to call it blog retrofit again the naming here is not the best but i just i want to be really clear like this is all the retrofit stuff because wait later when we go to use hilt i'm going to have like a retrofit module a caching module i just think that for learning purposes this naming convention looks is fine so now i'm going to use at get whoops at get and if you're familiar with retrofits uh you know what this is all about i'm doing a get request this is going to be a suspend function i'm just going to call it get you can call this anything you can call it like get stuff get whatever you want i'm just calling it get just for simplicity and this is going to return a blog network entity so that's the data model that we just built and inside of the get annotation i need to also write blogs so if you've again if you're familiar retrofit you know that there's this base url that we're going to need to pass so what i'm trying to do here is i'm going to be mimicking or mocking out exactly what this url is going to look like so it's going to be open api.xyz placeholder and then blogs so this is the last kind of section of that url that blog section okay so now we need a way to map these blog network entity objects to the blog objects because remember this is like our this is like our our project level model this this model is like a model exclusive to only retrofit so once we get that data from retrofit we need to convert it over to like our project model which is this this model right here if you're using clean architecture or if you use clean architecture this would be considered the domain model this is the model that never changes it's sort of central to the whole project and then you have other models that are built for different purposes inside of the project so we need a way to map this to this and also map this to this so back and forth between the two so i'm going to create a new package inside the main package directory call this util and then inside of util i'm going to create a class named entitymapper so entitymapper and this is actually going to be an interface so the first kind of generic parameter that we're going to pass up here is the entity and the second is going to be the domain model so remember like i said the blog model is basically the domain model this is what you would consider the domain model and then the other models are the entity model so that would be like this class right here this is a entity model it has a specific purpose for a piece of the application so we need functions to map one type to the other so function map from entity can be the first one so from entity this will take an entity as input just take that generic type of entity and it's going to return a domain model and then we need the opposite so we need to do map map two entity so map to entity this will of course take then a domain model and it's going to output an entity whoops and entity so one function to map to a domain model one function to map away from a domain model now i'm going to go back into our retrofit package here and build one more class so new kotlin file and this is going to be called network mapper because that's what it is it's going to be the class responsible for mapping the network objects to the domain objects so here we have network mapper and i'm going to use at inject on the constructor because i'm going to be providing this with hilt later on so just making sure to add inject here add a constructor because that's required to use hilt and this is going to extend that entity mapper that i just created so entity entitymapper and then i want to pass the two types so the the entity in this case is the blog network entity and then the domain model the domain object is that blog model class that i created now it's giving me a warning here telling me that i need to implement the members of that interface which are these two functions right here map to entity and map or map from entity and map to entity so if i just hold a control or alt enter on here go implement members get both of those functions now i can write out those use cases so both are pretty simple this one could just be like returning a new blog object in where the id is the entity dot id the title is the entity dot title the body is the entity dot body the image is guess what the entity dot image and the last one is category so entity dot category so these kind of conversions are actually really simple sometimes they're more complex but in this example they're very straightforward there's not really much difference at all between the entity and the actual model itself so we're gonna do the same thing down here i'm actually just gonna copy this because it's gonna be almost the same this will just be the network uh network uh or blog network entity so blog network entity and then just change all these entities to say domain model and we are good to go now as a last function down here this isn't part of the interface implementation but i'm going to add one more function for converting maps of objects and we're going to be using this later so function map from entity list and this will take a list of entities and it is and it's a list of blog network entity and it's going to output a list of blog objects so taking in a list of entities and outputting a list of blogs so i'm just going to say return entities.map this is a kotlin function i'll say map from entity and then just pass it so i'm just calling that function that i built up here map from entity and i'm calling it on every entry within that list so just converting all of those objects into blog out blog objects and then outputting that list okay so that's pretty much all the retrofit setup we need other than of course we do need the base url but we're going to get to that later but that's kind of generally all the retrofit stuff that we need so i'm going to close all of this and we're going to work on now the room stuff so the database caching stuff and to get started we of course need a dependency so let's get that dependency i'm going to just copy this actually from the source code for the project so if you just go to github there's gonna be a link down below by the way for you to easily grab this go to my dagger hilt playground or player ground and let's grab these room dependencies so you can go to any branch they all have this dependency click on build.gradle scroll down and grab the room dependency so this right here this is to use the room persistence library so that we can cache some data so pasting that in we have the room runtime the ktx extension and we have the compiler so the annotation processing now we're gonna do kind of the same thing that we just did with retrofit but with room so i'm gonna right click on the main package directory i'm gonna name this package room just to make it really clear what we're putting in here now i'm gonna create a new class this is gonna be the blog cache entity so blog cache entity again very similar to what we just did before this will be a data class and i want to add all of the kind of room setup to this so you got to annotate it with at entity we got to give it a table name this is the database table name that it's going to have i'll just call it blogs because yes there's going to be blogs stored in here i can open this up and i want to do primary key for the first annotation so primary primary key i can get that import say auto generate equal to false because we are going to be inserting the primary keys remember if you look at the network data let me just pull up a window here if you look at the network data remember we have primary keys so we don't want room to generate primary keys automatically we're already going to have those so we don't want to generate those so grabbing that primary key annotation now we need the column info annotation getting that this is to name what the column is going to look like in the database this is going to be the id column and then we just need the fields so the first one is the id and now i'm going to copy this a couple more times actually i'll copy it one more time because this is going to be a string and then i'll copy all of those ones together so this is going to be the title getting that so the title now this is a string oh get rid of the primary key we don't need that so copying this pasting it three more times when we need the body so getting that one we need the category getting that one and then we need the image and getting that one now some of you might be thinking well mitch why did you even bother making these cache entities and network entities when they look so similar to the data model and the answer is it's just good practice generally speaking the the entities from the network or the cache or wherever you're getting your data aren't necessarily going to look like your data model it's kind of just pure luck or i mean i built this so of course it's like convenient because they're supposed to be simple but usually they don't look exactly the same the data structures can be different so it's good practice just to make these entities you know go through the process make the mappers all that kind of stuff because typically you're going to have to do that so now we have our model let's build a mapper so let's go into room right click on the room package new kotlin file and this will be called cache mapper so again this is kind of the same process we did with the retrofit package here we built the blog network entity then we built the network mapper and then we built the class that is going to be able to do the actual network request we do the same thing here so we're building this caching mapper i'm going to annotate this with add inject because we're going to be providing this with hilt later on it doesn't take any constructor arguments but i do need to extend the entitymapper and in this case we have the blog cache entity and the blog for our two models so now we need to implement those members so alt enter on here implement members get both of those i'm gonna give myself some more room here and this is pretty simple just like the other one because our models are very similar just gonna do return a new blog model where the id equals the entity.id the title equals the entity.title we have the body equals entity.body category equals entity.category and the last one is image equals entity dot image so those are all the fields pretty simple going to copy that paste it down below change this to blog cache entity and then change all of these to domain model pretty straightforward very simple stuff and then again to finish this up we're going to build a function for mapping a list so map map from entity list this will take entities as input so a list of blog cache entities this is going to output a list of blogs so then return entities dot map and then we want to do map from entity and then just pass it okay so we have our entity model we have our mapper to map it to our domain model now we need to set up the stuff for actually building the database and interacting with it so right click on room go to new colon file this is going to be called blog database and if you're wondering about all this stuff like how do i know how to do this stuff this is all available in the documentation so if you just google you know i'll just bring up a window here so if you were just literally google like room database android and look for the link that takes you to the android developer documentation this one right here this would show you everything that i'm doing right now it's this is pretty known stuff it's it's not a secret there's great articles about it there's code labs easy stuff to follow also i have courses on my website so if you wanted some extra help like the documentation was just not doing it for you you could go to codingwithmitch.com and i have a room persistence library uh course right here for beginners so sqlite for beginners room persistence library it's completely free you just gotta register and you can watch it i show you everything from you know installing android studio all the way down to you know interacting all of the different interactions with the database you have inserts updates deletes um queries all that kind of stuff all right back to the project all right so building the database we need to annotate this with at database and reference the oops you get that import first then do entities equals the list of wherever those those entities are so these are like the database tables so just do blog cache entity because that's this right here it knows that it's going to be a table because i've annotated it with at entity so just telling it that yes here is my database table and we need to tell it what version this is for the for the database if you ever make updates to your entities or your database you just need to remember to increment this number but this is the first time we're building it so version 1 is fine now this is going to be an abstract class because there's going to be some some code generation that goes on behind the scenes that room is going to do for us so this is going to extend room database i want to initialize that open it up and now we want to do another abstract function to get a reference to the dao again in the background there's some code generation that goes on so it's not really going to be clear to you like what is happening here also we haven't built the dao yet we're going to do that next and the last thing in here is going to be the companion object and this is going to hold a reference to the database name so it's just going to be a constant value a constant string value named blog underscore db so what is this dao thing well we're going to build that right now so if we right click on room go to new kotlin file i'm going to call this blog dao and this is going to be an interface this is sort of analogous to what we just did with retrofit so if you look here we have this kind of interface that defines what functions retrofit is going to have available to it to get data it's kind of the same thing with the dao we have an interface here and this is going to be the functions that kind of tell the the database what it can do or the functions yeah that it's capable of using i guess so we can do an insert operation so that we can actually insert data and we want to pass a conflict strategy so what happens if there is a database entry that is the same as another database entry in that case what do you want to do so we say onconflict strategy and i can get that import dot replace so if there's a conflict we just want to replace the data i want to do suspend function insert i'll call it we want to insert a blog entity so blog cache entity and this is going to return a long the long value denotes what row inside of the database that was inserted so if it got inserted into row 3 it would return a 3 if it got inserted into row you know 10 it would return a 10. if it was not inserted and there was an error it would return negative 1. now the next function which is going to be the last one is going to be a query for getting data from the database so i'm going to say select all from blogs so this is the blogs table also notice that it was smart enough to do it autocomplete there so right there it's it's telling me that hey it looks like there's going to be this table named blogs it knows that because inside of the blog dao class we have a reference to the entity and that entity has a table name of blogs so it's smart enough to know hey okay there is a database table here and i'm going to be querying some data from that table now this will be a suspend function get and it's going to return a blog cache entity by the way if you guys don't know what suspend functions are you probably saw that i added them to the dal and i also added them to the retrofit class suspend functions are kotlin co-routines thing so it's basically saying that this is going to be or i have the option to execute this asynchronously i can do it on a background thread and you know if you've been around android development for any amount of time if you're getting data from the network or from the cache you need to do it asynchronously so that's why i'm marking these as suspend functions okay so now that we have our kind of room stuff set up we have our retrofit stuff set up now it's going to be time to build the the hilt part of this so we want to provide these things so they can be injected into our repository we want to build everything required for that so i'm going to create a new package inside the main package directory it's going to call this di and inside di i'm going to create a couple classes and these are going to be dagger modules so the first one is going to be a retrofit module and as you probably guessed yes we're going to be putting all of our retrofit dependencies inside of here so it's going to be an object because i want this to be a singleton i want the dependencies in here to be static more or less there's only going to be one instance of them so for a module i need to annotate this with at module and then for for hilt it's very easy we don't need to like add these modules to our components or anything like that anymore we just want to write install in and then tell it where we're going to be installing it this one is going to be an application wide dependency so i'm going to install it into the application component and keep in mind we haven't done any of the hilt setup stuff yet again i'll put a video up here linking to where you can see you know how to get started with hilt but i'm going to be doing it later on uh we're going to be there's not really that much involved we just need to add some annotations to some classes but this is kind of just like the starting uh the starting point where we need to define our dependencies so what's the first one the first one is going to be a singleton because it's going to be installed in the application component if you don't know anything about scoping by the way like you don't understand what this singleton thing is i encourage you to watch my hilt video on scoping where i explain all that i'll put a link up here and you can go check that out but i'm not going to be explaining it in this video so add provides and then just do provide the network mapper that's going to be our first dependency it's going to be of type entitymapper it'll be the two types that it's using are the blog network entity and then the blog object class i'm going to return a network mapper so that's the network mapper class that i showed you oh you know what i actually just realized that i made a mistake because i think whoops let's go into a network mapper i actually have an inject on on this constructor so i don't even need to provide this i can actually get rid of that dependency so just ignore that last thing that i did there and we will move on the next dependency will be also a singleton they're all going to be singleton i'm going to use at provides and do fun provide json builder this is going to return a gson json object and get that import so i want to do return json builder initialize that get the import do exclude fields without expose annotation you know actually you can add this or not add this it's just kind of the way i built this is i added the expose annotation so if there happened to be a field in here that was not annotated with ad exposed it would ignore it but we don't all of our fields are annotated with that expo so it really doesn't matter so you could if you wanted to technically you could delete all of this expose if you wanted to and then you could just delete this also it wouldn't really matter i'm going to leave it and i'm going to leave these in here because that's just kind of what i do it's just the the general pattern that i always follow so i'm going to leave it but just know that you don't have to and then call dot create and that's going to build that json object so the json object is responsible for parsing that json data and converting it into java objects which is what we want these are java objects so that's what we want to do we want to convert this to or we want to convert the json data into these objects that's what this builder is for now i'm going to copy the singleton and the provides paste that down below i'll do fun provide retrofit and i'm going to take the json object as input so hilt is smart enough that it can find that that dependency and accept it as input here and i'm going to return a retrofit builder object so getting that that import now i want to do retrofit retrofit dot builder and do dot base url so here's that base url that i was talking about earlier it's going to be https open api dot xyz slash placeholder and make sure to do a trailing slash so this is that this is that url up here that i was talking about open api.xyz placeholder and then the last part here is blogs which we can find inside of the retrofit here so that's this this part right here the last part is blocks so coming back to here now we need to add a converter factory so we need to tell it how to convert this data how to convert that json data into those java objects so i can do json converter factory dot create make sure to get that import dot create and then pass that json object that i built up here so it's passing this this json object that's being returned into the builder so that it knows how to convert those objects now the last dependency here is going to be the actual retrofit the blog retrofit interface right here so we're going to build this with retrofit so singleton provides do fun provide blog service i'll call it and it's going to take the retrofit builder as input because it's being returned up here so we're going to take that as input i'm going to return blog retrofit so return retrofit dot whoops retrofit dot build and then do dot create and then reference that interface so blog retrofit class.java and that'll build our retrofit instance so now that we have this we can inject this into things so like just as an example let's just create a new package here i'll call it you know repository inside of repository i'll create a new class called main repository it'll be a class so if i wanted to i could annotate this with add inject add a constructor and do private value retrofit and pass that blog retrofit object it would it would be smart enough hilt would be smart enough to inject this where it's required so this is kind of just a sneak peek into how we're going to be using this i'm going to be i'm going to delete this for now because i don't want to confuse you we're going to come back and work on the main repository later so let's just delete that so that is going to be the retrofit module now since we built a module we haven't really finished setting up hilt and setting up dagger yet i'm going to do that right now before we continue with the other modules again i have a video on how you can do this i'll put a link up here just how to like get started with hilt but now i'm going to show you everything that you need to know so the first thing that you actually want to do is build an application class so i'm just going to call it my application and it's going to be a new class so my application it's going to extend application and we need to add a special annotation up here hilt android app and it's giving me a warning saying i need to add this to the manifest so that's what i'm going to do go into the android manifest add the name parameter and this reference my application so i can close that i'm done in there and that should be good to go so that's going to kick off kind of all the dagger the hilt code generation stuff it's going to generate all of the components that we need so that we can start using hilt much much easier than what you would have to do with dagger before now i'm going to go into main activity and annotate this with android entry point therefore i can inject things into this so like for example if i wanted to inject my retrofit my blog retrofit instance i could inject this i could inject the gson object you know json json it would know how to build that too i'm not going to inject those but just kind of giving you an idea of what i could do if i wanted to now that we've kind of set up dagger or set up hilt sorry so we've got our retrofit module i'm actually going to close all these just kind of clean things up a little bit now i want to do the same thing with the room dependency so we have like our blog cache entity we have to build our dao with to build a database we have to build our cache mapper actually we don't have to build the cache mapper because it's got the inject on the constructor so that's fine but what i'm trying to say here is we need to build another module for all of the room stuff so let's do that let's right click here and i'm going to call this room module just so it's really clear what this what this is going to be doing it's going to be an object just like the other module annotate this with that module and i want to install this into the application component so install into the application component.class so i'm installing it into the app wide dependency component the one that will exist as long as the application is alive there are different components you know there's an activity component there is an activity retained retained what is it called activity retained component there's a fragment component those components i explain in my my video on scoping so i encourage you to go check that out but we're going to be just using or installing into the application component for all the dependencies in this video so at singleton at provides do function provide oh i almost did it again i was going to provide the cash mapper but we don't actually need that because it's got the cache mapper already has inject on the constructor so i don't need to do that just trying to break a habit so we're going to provide the database first so i'll do fun provide blog db and this will take the application context as input it's going to need that to build itself that's going to be a context object and i want to output a blog database so getting all of those imports now i want to return whoops not sure why the tabs got messed up there return room dot database get that import database builder i want to pass the context i want to pass the blog database class so just referencing what we are building now i want to pass the database name so that's a constant inside of our blog database so blogdatabase.database name now i want to call fallback to destructive migration and then dot build and that's going to build that blog database object so that's just like essentially just building the database inside of the project so now we can we can use this database and get access to the dao so i'll copy singleton and provides one more time paste it down below and i'll do fun provide blog dao this will take the blog database as input and it's going to going to spit out a blog dao and i just want to do return blog database dot dao and that's how we get a reference to the dao that's what we're really after here we're after the dao that's going to give us access to all the functions that we can use to interact with the database so with that we have our room module and we have our retrofit module so we have our two modules for providing the dependencies for the caching kind of dependencies and then the network operation dependencies now we're almost ready to start working on the repository before we do that we need to build a couple like utility classes actually one utility class more specifically so i'm going to go into the util package go to new class and this is going to be called data state now if you're familiar with my courses you watch a lot of my courses you know how i typically build a data state class and this is not going to be typically how i build the data state class i greatly sort of simplified this so that because you know this is just a simple it's a simple video it's a simple example usually i have a much more complicated data state class that handles all kinds of different stuff different state messages state errors data coming in all kinds of different stuff this is just going to be a very simplified version so if you want more information on kind of like a more complex example i definitely encourage you to go to codingwithmitch.com and maybe check out my clean architecture course so if you go to courses you go down to clean architecture that's a really good course it shows you a much much more advanced look at kind of all of the things that i'm talking about in this video so check that out if you're wondering about it you don't want to pay for it you can click it and watch the course demo and see kind of what it's all about now let's get back to the project so this is going to be a sealed class and if you don't know what a sealed class is just go to my youtube channel and search sealed class and i'll explain what that is pretty sure i have videos on sealed classes it's going to take a generic type of r we'll say it's going to have two or three classes inside here the data class for a success case so what happens if a network operation is successful or what if some kind of operation is successful in that case i want to return some data so we'll just call it data and it'll be of type type t and it will return data state and type t and we'll just initialize that so that's the first one next will be an error case so data class error value it'll take an exception and we just want to return again data state but of type nothing we're not returning any actual data there we're just returning and that something went wrong and maybe pass the exception pretty simple last we'll be loading this is not going to return absolutely anything it's just to to basically tell my ui to show the progress bar oops i kept typing data database but i need to do a data state and this again will be nothing because we're not returning anything so if you go to like the android documentation this is a pretty common class that you see i'll actually put a little comment up here for you from the uh from the source code so it just says this is not my standard datastate class which i just explained to you i want to simplify simplify things for this hilt example my standard implementation is much more complex so you can see the link to an example of one of those so pretty simple basically just we do some kind of operation was it successful was there an error should we should we be showing the progress bar that's it all right so we are now ready to work on the repository so i'm going to close these we're going to go into main repository and i'm actually going to pull up an architecture diagram for those of you who have never seen this architecture diagram this is kind of the standard architecture that is recommended on the android documentation you have your activity or your fragment or in other words your view you have a view model which manages kind of all of your data you have a repository and then the repository has access to the different data sources um usually it's like the room persistence library which is what you see here or a remote data source using a library like retrofit so this is exactly what we're going to be doing right now so we've kind of set up the room stuff we set up the retrofit stuff now we're going to work on the repository so i want to add a constructor and we're going to pass a couple constructor arguments one is the dao the next is going to be the retrofit instance another one is going to be the cache mapper and the last one will be the network mapper and i forgot to add a value right here so these are all the dependencies that we're going to need to do the network request to cache the data and then to emit that data back to the view model which will then emit it to the ui so this is going to have a single function suspend function we'll call it get blogs because that's what it's going to do it's going to get some blogs from from the open api website it's going to return a flow which is a kotlin co-routines data structure a very beautiful data structure i love this ever since kotlin co-routines came up with flows and channels i absolutely fell in love with it it makes all this stuff much easier than it used to be you just have to put like live data in here use mediators set it remove it all kinds of really complicated things and now you're going to see just how easy it is to emit some to do a network request cache it to emit it to the ui do all this stuff it makes it way way easier so we're going to use this flow builder which is another kotlin co-routines dependency and then the first thing that i want to do here is i want to emit a loading state so i want to do datastate.loading so what this is going to be interpreted as in the ui is the ui is going to see okay we got something emitted oh it's telling me to show the progress bar so i'm now i'm going to show the progress bar that's what it's going to do i'm going to add a 1 000 millisecond delay here just so you can actually see the progress bar for at least a second because this network request is going to be so fast that you probably wouldn't even see the progress bar so i'm just going to delay it 1000 milliseconds of course don't do this in production never just delay anything it's there's absolutely no point other than for teaching purposes so i'm i'm delaying this 1000 milliseconds now i'm going to add a try catch block here to catch any exceptions that might happen when we do all of our kind of work that we're about to do so i'm going to grab the network blogs first and because this is a suspend function i don't need to do any kind of fancy threading i don't need to worry about doing this on a background thread this is automatically going to be asynchronous and when you later when we actually launch this off i can define what thread pool to to launch this in so that's going to be done in the view model later so just for now just notice how simple this is all i need to do is call blogretrofit.get and that's going to retrieve that data on whatever thread i launched this code routine on which we're going to be doing later now i want to get blogs from those network blogs so we're going to be using our network mapper whoops network mapper to map from entity list because that's what we just got we just got a we just got a list of entities a list of network blog or what are they called let's go back into here and just see their blog network entities i just got a list of those now i want to convert those to blog objects so that's what i just did now i want to loop through each one of those blog objects so for blog in blogs and i want to insert that into the database cache so i can do blog dao and i need to use my cache mapper here because we're mapping a a blog object from our model so this right here we're mapping this to a blog entity so if we look here and go into room notice the dao takes a blog cache entity as input so that's what we need to do we need to map it from a blog to a blog cache entity so that's what we're going to do so i'll do map map from from our map to entity is actually what we're after here passing that blog object and then it's going to get inserted into the cache so now to finish things up what i want to do is i want to query all the blogs that now exist in the cache so all the ones that i just inserted so i can say value cached blogs equals blog dao.get so retrieve all those from the cache and now i want to emit those to the ui so i want to say datastate.success.success and pass cache mapper cachemapper.map from entity list and then pass that list of cached blocks so just emitting that data to the ui after it's been retrieved from the network sent to the cache retrieved from the cache now i want to show that and if there's an error i can just do emit data state datastate.error and just pass that exception so e and that will be that's our use case so that's the one thing that this repository needs to do is just do kind of all of these things and emit the data to the ui okay so the last class that we're going to build before we work on the activity is the view model so i'm going to right click on the main package directory and create a new package named ui i'm going to move main activity into the ui package you could also move the application into the ui package but i'm just going to leave it here it's it's common to put it into the ui package or it's pretty common also to just leave it out so you can do whatever you like it's not really not really important so instead of ui i'm going to create that main view model class so if you're familiar with dagger if you've used dagger before you know that injecting and providing view models was not the easiest thing to do but with hilt it is very very simple and i'll show you just how simple it is so all i need to do here is go down below one line and i need to do view model inject and then i just add the constructor and that's it that's all i need to do to inject a viewmodel of course i need to extend by the viewmodel class and actually i i did lie technically there's one thing that i do need to provide here that's the to use the assisted annotation and provide the saved state handle so private value saved state handle save state handle and all this stuff can be found in the android documentation by the way so if you were to go i'll just actually bring up a window here that was just a search you know dagger hilt and go to the android documentation right here dependency injection with hilt you can go down to the view model kind of section so if you go to hilt and jetpack integrations it'll show you how to inject stuff how to inject view models with hilt and it gives you an example here so getting the dependencies and then just creating a view model and uh and using that view model inject annotation very very easy so we've got the saved state handle which we're actually not even going to be using but the thing that we are going to be using is the main repository so we need to make sure that we add that as a dependency to the constructor of our viewmodel now you might be thinking but mitch you didn't add you didn't add an inject here and you're right i didn't but that's because we're going to be providing this in a module later on well actually we could just do it right now so let's go into the di package and create a new class this class will be named repository module and it's going to be an object so annotating this with app module so you've seen this a couple times now we want to install in the application application whoops i can't spell application component class so there we go we have our module it's going to be installed in the application component we need to annotate it with at singleton at provides now fun provide main repository repository it's going to take a bunch of constructor arguments so we need the dao blog dao we need the retrofit object so blog whoops we need blog retrofit blog retrofit we need the cache mapper so cache mapper and we need the network mapper so remember if you take a look at the repository these are all the constructor arguments these are the things that we need to build this repository object so this will be of type main repository and then return main repository the blog dao the retrofit object the cache mapper and the network mapper there we go that's now our repository so now now this can be injected and it's going to be injected into our viewmodel so i know at the beginning of this video i said that i was going to be using mvvm architecture but i'm actually going to be using mvi architecture i'm gonna be using mvi because i think that mvi is it's it's literally identical to mvvm it's just better because it has better organization and you're gonna see why here so if you don't really know about mvvm you don't really know about mvi whatever the case it doesn't matter i'm going to show you everything that you need to know just know that mvi is essentially exactly the same as mvvm it just had has added organizational features so it's just better because you it's very descriptive of what you want to do so you're saying do this thing and get this input it's just very very much more organized i guess that's the best way to put it so let's let's do that so when using mvi architecture the first thing you want to do is create a state class i'm going to call it main state event and inside of this state class you want to describe all of the different events that you can fire off well in this application we can only fire off one event and that's get blogs event so basically when this gets fired off we want to go and retrieve the blogs from the network that's the only thing they can do sometimes i like to put a none event so main state events just like maybe to clear things up if you ever need to not really necessary this is an optional thing you don't really need this so this is this is the added organization that i was talking about so now up at the top i'm going to create a data state object so private value data state and this is going to be a mutable live data object going to return data state wrapping around a list of blog objects so getting all of these these imports that's going to be a new mutable live data object now we need a way to take in state events so main state events and convert that into data state that's essentially what we need to do i accidentally deleted that give myself a little bit more room to give you a better view so i'm going to create a getter function first of all for the data state so just do data state this will be live data live data wrapping around data state wrapping around a list of blog objects getting all of those imports and we need to add a getter so just get equals the underscore data state so this is private and then this is the accessor for that data state now we need to write a function for setting the state event interpreting the state event and then doing whatever we need to given a certain state event so function set state event is what i'll call it it'll be a main state event as input i want to do view model scope uh looks like i'm actually missing some dependencies so we're going to go into the build.gradle file and add a couple more dependencies here so again if you go to the source code and you go to the build.gradle file just make sure to grab these dependencies you'll actually need this one to instantiate the view model anyway so grabbing the fragment ktx one and also this lifecycle live data ktx so these two dependencies right here so copying those going to build.gradle and pasting those down below hitting sync and again you can get that from the source code for the project so that will give me access that should give me access to the view model scope view model scope there we go and then i'm going to launch this co-routine so i can say when the main state event is well we only have two cases so is get blog events do the arrow open this up in that case what do i want to do i want to do main repository dot get blogs and this should be actually get blogs but it doesn't really matter we can just do get blog do on each and then what i want to do in in the case of data state coming back so i can pass data state into the lambda here in that case every time i want to do datastate.value and just set that so set that to the data state and i want to do launch in the viewmodelscope so view modelscope and that is all i need to do this is telling me that i need to annotate this with the experimental core routines api so i'll do that now i can also use the none case but it doesn't matter so i can say is none do the arrow and then just say you know who cares we're not actually using that so i hope that you can see that if we had like a whole bunch of state events like a whole bunch of different state events this would be a much more organized way to do things because you have you know this one set state event function that i'm going to call inside of main activity and then given whatever state event it's going to get the data and set the data now of course that would mean that this can't just be a list of blog posts because you might be returning some different types of data so you'd want to have some kind of a generic in here like some kind of generic wrapper but this that comes down to what i said about the data datastate class in this video i greatly simplified my datastate class usually it's much more complex and it can handle different types of data but just to keep things simple here we have a single type of data that it's going to be returning which is the list of blogs and we only have you know one state event so it's very simple if you want to see like a more realistic more complex example i definitely encourage you to check out the courses on my website more specifically the ones you'd probably want to watch is clean architecture if you can get through it it's definitely a more advanced course but that would be the one to watch or if you want to know like an mvi introduction this is a great one even beginners can watch this one model view intent architecture this is a beginner's course and after that i would say powerful android apps with jetpack architecture that is that's definitely the one you want to do next if you think clean architecture is a little bit too difficult for you now let's get back to the project all right so we are nearing the end we've got kind of all the pieces in place i can close all this stuff and now we just need to go to main activity and actually kind of fire off the request and get the data and display it so first of all let's uh let's do i'm going to type log t now you might not have this this shortcut oh i've already got it up there actually you might not have that shortcut that's a template that i built so you might have to type this out manually private value tag string that's just for debugging and now we're going to instantiate the view model and this is very easy with hilt you'll see just how easy in a second here so i'll say private value view model this will be of type main view model and we're going to use by view models by view models and i don't need to even specify the type here this is going to tell me that i'm using an experimental feature so i need to add the experimental feature annotation this is the easiest way by far that i've ever seen to instantiate view models and i love it in case you're wondering which dependency is required for that it is the fragment ktx dependency right here so you need to have that in your build.gradle file in order to use buy view models to instantiate the view model notice that i don't have to annotate it without inject i don't need to use a factory i don't need to do anything it's automatically going to know how to build that view model so it's a beautiful thing now let's come down and build a couple functions so first one is going to be subscribe observers and it's going to take nothing it's going to return nothing this should be fun i forgot to add the fun subscribe observers so i want to do viewmodel.datastate.observe i want to get the view lifecycle owner which is just the activity observer and inside the observer we're going to see data state coming in from that flow from that flow emission so now i just want to do a when statement so when the data state is different things so is is let's say datastate.success remember those different cases that we have here it's going to be a list of blog posts coming in so getting that import arrow and open this up in that case i want to call some functions so i haven't built the functions that i'm going to call yet so let me just write out these other cases and then we will build those so this is going to be the error case and this is going to be the loading case so we need to kind of add a to do here we need to call some functions which i haven't built yet so we're going to build those before we come into here so let's go build those functions so we want to cover kind of all the different cases so the first one is like we want we need to display an error so private fun display error if there's ever an error returning from our request then we want to display that so i can say if the message does not equal null so if this there is an error message then i can just do text which is the text view inside the layout let me actually just show that to you so if you go to resources go to layout activity main this the id for this text view which is just kind of centered well it occupies the whole page this is called text it has an id of text so that's what i'm referencing right here so text dot text equals message equals message i guess that's what i want if the message isn't null otherwise i can say text dot text equals unknown error so if there's an error and it doesn't have a message just show unknown error and that should be fine the next function can be loading so display the progress bar so private function display progress bar and it can take a boolean is displayed so boolean boolean and then i can just do progress bar so the id of the progress bar inside the layout is just progress bar set the visibility equals if is displayed then we know that the visibility should be view dot visible otherwise we want to do view dot gone so if if is displayed is true display the progress bar otherwise hide the progress bar pretty simple now the last function is going to be for handling the actual data when we actually get some data coming in so private function append blog titles so remember if you take a look at the data structure i'll bring up those the wrong window i'll bring up this if we look at the data structure here remember we have a bunch of stuff we have the primary key we have the title the body the image the category but the only thing that i'm going to do in this application is i'm going to extract the title so we need to extract the title from the blog objects and then just display them in you in the ui so that's what we've done in the sample application here so let's uh let's write a loop to do that so it's going to take input blogs it's going to take a list of blog objects open this up i'm going to do value string builder equals a new string builder object now i want to say for blog in blogs i want to extract the title and append that to this to the string builder so sb.append blog dot title and then just append also a new line so that should handle all that and then at the end of the day you want to do text dot text equals sb.2string so just going to append all of that data okay now coming back up to our subscribe observers function now we have some functions that we can actually call given these different cases so i can say display progress bar that's going to be false first of all basically if there's an error or if there's data coming in we want to hide the progress bar that means that everything is complete otherwise if if um if it says loading then we want to say display progress bar set equal to true so the only way that it should show is if we get that loading status otherwise we want to hide it so in the success case that means we actually did get the blog so i could say append blog titles i can say datastate.data just to get all that data from the data state and if there's an error i want to call display error and in that case i want to do datastate.exception and get the message from the exception that's going to be returned now the last step here is going to be to subscribe our observers and of course we want to fire off that event so i want to do set state event and call the get blogs event and initialize that so that is how i don't need to initialize that that's how we fire everything off that's this is kind of like the this is the the payout for mvi architecture if you ever want to like you know just fire off a request a very specific request all i need to do is call set state event and call the respective state event and everything will be handled so with that that should be everything we need so if i run this now we should see our blogs being retrieved from the from the network cached and then displayed in the ui there we have the app launching we see the progress bar for one second and then there is the data being displayed so everything is working exactly as expected so that's it that's the example and that's that outlines a very very common scenario that happens all the time in android development get a network get some network data cache that network data display it in the view model and we've used uh mbi repository pattern with a view model we've used a network layer with retrofit we've used a room layer or caching layer with the room persistence library we have injected all of our dependencies into the repository we've injected the viewmodel we inject the viewmodel into the activity all of these different things and this is a very very common scenario like i said you see this all the time so hopefully if you're kind of a beginner you don't know a lot about architecture you don't know a lot about dagger maybe you've never seen dagger before this was a more i know it was long but i really wanted to kind of put everything together for you so that you could see a real example of like a really common scenario that happened all the time so if this video helped you please take the time to go down there go to that little gray thumbs up i think it is and turn that into a blue thumbs up youtube does not know that this video was helpful to you unless you tell it that it was helpful please i would really appreciate that if this video helped you at all also as a reminder if you want extra help with some of the more advanced concepts that i was mentioning throughout the video head on over to codingthefish.com and check out courses i got free courses i got paid courses for all levels of android developers from beginner to quite advanced i definitely encourage you to come here watch some course demos they're free so if you wanted to like check out the clean architecture course just click on it go to course demo watch the course demo it's free if you're wondering what people are saying about my courses like if they find this stuff helpful head on over to more and go to testimonials and you'll read from there's 748 testimonials at this point so tons of stuff to read about try and find a bad one there's not very many bad testimonials in here i got an average rating of 4.98 so i'd be really surprised if you were able to find a bad testimonial in here you know a lot of these people have been getting jobs in my discord channel i hear people saying all the time that they were able to get a job thanks to my courses so if you're looking to start a career in android development or advance your career in android development definitely consider the membership as always thanks for watching and i'll see you in the next one you
Info
Channel: CodingWithMitch
Views: 42,849
Rating: 4.9658508 out of 5
Keywords: dagger hilt android, dagger hilt android tutorial, mvvm android, mvi android, mvvm repository, mvvm repository pattern android, hilt retrofit, room persistence library, android room persistence, mvvm example android
Id: 8vAQrgbh6YM
Channel Id: undefined
Length: 60min 24sec (3624 seconds)
Published: Tue Jul 14 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.