Full Guide to Manual Dependency Injection + Removing Dagger

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey guys and welcome back to a new video did you ever have slow builds due to dagger or did you ever have Gradle errors because of dagger what if I told you that you don't even need dagger 4 dependency injection on Android and there is actually an easier way without any extra complexity because you can just do that manually I think that dependency injection on Android is very often just over complicated because in the end dependency injection is really just about passing objects to other objects so in this video I will show you an alternative to using dagger which is just manual di it doesn't require any Library it makes your builds faster and you won't have any build issues because of dependency injection and this video is sponsored by myself because you can still get 30 discount on all my premium courses including the new released testing course bundle so if you want to learn about automated testing on Android in the real world how that works then click the first link in this video's description to secure the course because the discount will expire in a few days getting back back to the video I prepared a little project here in which I've set up dagger Hilt and we will now migrate this product step by step to use manual di instead and to completely get rid of dagger so you can see here in main activity we have the typical Android entry point annotation which we need when we inject dependencies using dagger into an Android components such as an activity in this case that is our main view model which I've created so I initialize that with Hillview model in order that hit also created the viewmodel factory that is behind this view model or that needs to be created for that then if we take a look in the main view model then you will notice here we have the Hillview model annotation with an inject Constructor where we simply inject an auth repository this is really just an empty dependency so you can see how this would now work in your project to migrate from dagger to not using dagger at all or just to start with no dagger in the first place if we take a look in the auth repository that's just a normal interface and abstraction of our off repository implementation which we'll find here which users done an API from retrofit and then just has a blank empty login function which just prints something so we can really see that the correct object is injected later on and typically when we want to inject dependencies with the dagger build then we also need this application class which is annotated with Hilt Android app and we need a so-called module at least one module which is here the app module in which we basically bundle and Define our dependencies how these are created because that is something Dagger of course needs to know in order to inject these instances in our other classes like our review model later on so as you have noticed that is quite a lot of setup to manage your dependencies and to be able to pass these two other objects because as I've said that is the whole point of dependency injection that if we take a look in a review model that we pass in our dependencies or objects from the outside into such an object so we just remain flexible with that because if we were to test this main view model for example then we would like to pass in a different instance of this auth repository which is just optimized for testing and that that of course only works if we pass this in the Constructor here and that is exactly what manual dependency injection means we just pass the penalties we just pass object into other object Constructors and I really don't know why this topic is so overcomplicated on Android for some product dagger might be a great solution and I personally also just get used with it I'm fine with using it but it's really not the Holy Grail as most people portrayed as so I just want to show you an alternative and then you can decide what you prefer for your own project and I would like to start migrating our app module because that obviously heavily relies on dagger here we have this module annotation installed in annotation with a component we have provided Singleton with these functions which is something that is very specific to dagger and if we now want to migrate that we can just create a very normal app module class down here so class app module and that will take in our application context in the Constructor since we of course we want to have access to that we also do have access to the application context here in the app module from dagger so we also want to be able to have access to that in our own custom app module we want to open this block of code here and what do we now put in here in the end we do exactly the same as we do up here we need to Define how our dependencies are created that is something you obviously don't get around but when doing manual dependency injection what I like to do is I like to create a little abstraction for this app module so I want to have an interface app module and in this interface I Define first of all which dependencies we have that are provided in this app module and for that let's uncomment this dagger module so we don't get any naming conflicts in here in this app module abstraction we just Define we do have an auth API just like in our dagger module so we provided the auth API here and we Define that we have an auth repository we want to provide like this and this app module the explicit implementation of that will then be this implementation and I will also talk about why I abstract this module here in a moment this would then implement this app module interface and here we can then override these two fields on the one hand we need to specify how our auth API is created and here for manually I I just like to do this by lazy so this objects are initialized and created the first time we access these somewhere in our project so in here we can just copy our retrofit code paste it down here and then uncomment this again and format this we can also get rid of the return statement and then we shouldn't have any other issues and next up we want to do the same for auth repository also buy lazy and in here we then create oauth repository implementation with the help of over already created op API and that is our very own custom app module implementation which is already lighter than what we created with dagger here however one thing is already considered with dagger Hue which is not considered with our own approach and that is that we want these dependencies to be Singletons so we don't want that there are two of apis are product that there are two auth repositories no we want to reuse the same instance across all of our classes where we inject these classes so how do we achieve that we can access this auth API in this auth repository from all of our project basically and how do we make sure that these dependencies are also active as long as our app is active and for that we can use our application class so we still need this just that this time we don't want to annotate this with Hill Android app but rather create our own annotation on the one hand we want to have a companion object which allows us to always access this app module and its dependencies so here we have a late inner variable app module which is just an app module and in oncreate so when our app is created this is only called once during our whole application's lifetime in here we want to initialize this app module with app module implementation and we pass in our uh just this because that is the application context and now since this app module here is inside of a companion object and therefore like a static object we can access this from across our project and if you have multiple modules in your product you can just create this inside of this app class as well of course if you're creating this from scratch make sure that you also register this application class in your manifest by adding this name attribute here with your app class but then you already settled okay now let's take a look at the rest of our product because we're not done yet obviously within dagger we inject the dependencies here in our review model we have this Hillview model annotation which creates a remodel Factory and then we have this inject Constructor where we inject over auth repository we don't want this Hilby model annotation anymore and we also don't want this inject Constructor anymore we just want to have a normal Constructor in which we Define the dependencies we need in this main view model and here you have two options you can either just specify the dependency as it is in this case it's just a single one um so we can leave it like that but if you have a lot of dependencies here in a maybe a model which are all provided in this app module you can also pass in the app module implementation right away one thing that dagger did for us that it right now does not do anymore is it created a view model Factory so when we create an instance of viewmodel in Android then we can't do it right away by just calling it Constructor no we need to create a so-called viewmodel Factory and in fact this looks somehow like this so we have a main view model Factory which is a viewmodel provider dot Factory and in here we can hit command o control o and override this create function in which we then would need to return our main view model instance with the corresponding dependencies that run to pass and I cast that as T that was quite a lot of effort for every single view model in our product which is why I like to make that a helper function to easily create these view model factories with not a lot of code for that I would like to go into inside of presentation call this view model Factory helper for example make that a normal file just let me add that to git and this feature will look a bit more complex but I will explain everything after I have written it so first of all we have a generic function where we get some some type of view model passed in I want to call this view model Factory and in here we pass in a Lambda called initializer which then returns such a view model instance and this whole function then returns a viewmodel provider.factory in here we then return an anonymous class object v-model provider.factory and we override this create function again I'll make sure to use the overload that just takes in the model class and here we can then return or initializer as T so what the heck happens here first of all we specify that what our initializer function returns so the function that will then create our review model later on needs to inherit our view model so we can't call this we can't make this initial as a return classes that don't inherit from viewmodel and this whole viewmodel Factory function then returns exactly such a factory where it just holds or initialize a Lambda that we pass in and converts that to T So to some type of view model and the way this now works is if we go to our main activity instead of Field view model and instead of this Android entry point annotation which we can remove we now just want to have the normal view model composable function and you can see it already takes in such an initializer we don't want this by the way but rather want to have this Factory so we can specify a view model Factory you can see your model provider Factory which is exactly what our helper function returns and here we say via model Factory like this and in here in this Lambda we create the instance of our review model so make your model and now we need to pass in our auth repository how do we now access that well we do have our my app class which has a companion object and a public app module so we can just say app mode uh my app the dart appmodule dot auth repository and there we go we still need to specify the type here that we are dealing with the main view model but then that is working exactly the same as before so all of our dependencies are managed here in our app module you can easily extend this just add some more dependencies here you can easily add more modules which you initialize in your application class and in case you want to scope a specific set of dependencies a specific module to a smaller component than your whole applications lifetime for example to only have these dependencies active during a specific view model's lifetime then in that case you would not need to create this app module or this module whatever you're dealing with inside the application class but rather in your viewmodel class and then make sure to set it to null again when the viewmodel is in on clear here so when the viewmodel is destroyed and that way you just have the penalties that are scoped to one specific view model and which are destroyed when the v-model is destroyed but that is definitely something you don't need very often that you need to scope dependencies from all audio adjust to a specific view model but if you need that then this approach totally supports that as well and before I want to tell you why I made that an abstraction here this interface it's also get rid of that I want to try this out and see if everything works I will launch this on my device nothing special will happen here but let's just hope that we see the print line statement from our auth repository here that we'll log in because then everything worked then we have a working view model so let's take a look and lock out make this a bit bigger and I click on this button now and yes you can see there is logging in so our dependency injection works perfectly fine this now has the exact same benefits as if you're doing this with dagger yes dagger might be a better fit for some more advanced use cases and it might just provide better and more structured solutions for complex problems but really in most cases you're totally fine with implementing dependency injection manually like this and the good thing is now you're completely independent of capped so of the cotton annotation processor which is what takes so long with the dagger when it just builds and generates all these classes behind the scenes so coming back to our app module as I've promised this app module interface doesn't seem to be necessary right now in the setup but it is necessary as soon as you get into testing so let's imagine you pass this whole app module somewhere for example here in the view model um just app module like this and then in here you refer to the dependencies like this app module.auth repository.login then if this is an abstraction you have the option to replace this with a different set of dependencies for testing because very often for testing we don't want to use our real API and make real API calls that take a while that could fail because of the API we just want to replace certain dependencies with other dependencies that are optimized for testing and that only works if we have the option to pass in a different instance of app module which is why I made that an interface but for your production app this app module implementation is all you need let me revert that very quickly here because I like this one more now I hope you enjoyed this video and this showed you A New Perspective of how we can do dependency injection on Android because it's really just about passing something to a Constructor and yes dagger is doing exactly that under the hood as well so if you enjoyed this you will definitely also enjoy my new testing course bundle check it out down below you still get a discount for a few more days and other than that I will see you back in the next video have an amazing rest of your week bye bye thanks [Music] foreign
Info
Channel: Philipp Lackner
Views: 32,384
Rating: undefined out of 5
Keywords:
Id: eX-y0IEHJjM
Channel Id: undefined
Length: 14min 49sec (889 seconds)
Published: Wed Aug 30 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.