Let’s learn together the basics of setting up an android project with Hilt! Remember, Dependency Injection means that the dependencies of your various components are no longer provided internally within the actual classes, but are being created externally.
While in the previous parts we have gone through the definition of DI, why do we need it and what are its benefits, today we are going to actually get our hands dirty and start integrating Hilt in a very basic android App.
Why Hilt?
Hilt is a JetPack component, thus has a lot of official support coming for it. Moreover, Hilt is build on top of Dagger (the big scary DI framework tool) and ensures compile time correctness and runtime performance.
Now, be it Hilt or Dagger, such frameworks reduce boilerplate code that is added when doing manual Dependency Injection in scalable projects by enforcing the use of annotations. They rely on annotation processors to auto-generate code at build time, therefore creating and optimizing your whole dependency graph.
But why is Hilt built on top of Dagger?
As many of you might know, Dagger has a steep learning curve and is usually pretty difficult to grasp for new developers. In this sense, Hilt comes to the rescue as one of its traits is to hide most of the complexity and boilerplate code that usually revolves around Dagger. You can imagine the Hilt library as being a friendly handle to the Dagger library.
In addition, Hilt and Dagger are interoperable thus allowing you to transition from one to another in a smooth manner.
Content
- Setting up Hilt
- Using Hilt
Alright, before jumping in, let’s establish some basic requirements, just so you know what to expect:
- Basic Android programming experience
- Basic Dependency Injection understanding
1. Setting up Hilt
Let’s start by adding the plugin and its dependencies to our app’s build.gradle
:
Next off let’s add the classpath dependency in the project’s build.gradle
:
Finally, let’s annotate our application class with @HiltAndroidApp
so that Hilt can start creating the dependency graph (and initiate the code generation for our injected classes) and also to inject the application context wherever we need it.
2. Using Hilt
Alright, now let’s tell Hilt that we want to create an injection container for our MessageActivity
by annotating it with @AndroidEntryPoint
. We are basically telling Hilt that in this activity we will be injecting member classes, more specifically a fragment.
As you can see in the above gist, in order to have our fragment injected, we mark our field injection with the @Inject
annotation, thus telling Hilt that we need this dependency injected within this activity container.
Now, Hilt knows it should inject the fragment, but in order for this to work we need the fragment to be injectable, so we mark the constructor as injectable with the @Inject
annotation:
Now you might have noticed that we have marked the MessageFragment with the @AndroidEntryPoint
annotation, but why is that?
Well, as we need to inject the viewModel inside the fragment, similarly with how we tackled the activity, we also must annotate the MessageFragment
as an injection container with the known @AndroidEntryPoint
annotation. This way, Hilt knows we will be injecting stuff within.
Perfect, the only thing left to do here is to inject the MessageViewModel
, but how?
Well, it’s easier than you thought because in the fragment the only thing left to do is to initialize the viewModel just like we usually do, using the syntactic sugar provided by JetPack by viewModels()
. Forget about any viewModel factories because this is taken care of by Hilt now.
Now, it’s important to note that the JetPack viewModels cannot be injected out of the box with Hilt as it only supports supports by default: activities, fragments, views, services, and broadcast receivers. In order to inject viewModels, we have initially imported this dependency: androidx.hilt:hilt-lifecycle-viewmodel
.
This extra dependency allows us to mark the MessageViewModel
as injectable with the special annotation @ViewModelInject
. Now, in order for Hilt to know in which injectable container it should place the MessageViewModel, we specify it to be the activity via the @ActivityScoped
annotation. This means that the viewModel will live as long as it’s activity will.
Perfect, our MessageViewModel is injected. As you can see, we have injected the viewModel’s constructor thus providing two dependencies, the application context via the @ApplicationContext
annotation – which basically means that we can get the app context wherever is needed, we just need to specify the correct annotation – and the MessageRepository
.
Now, for the repository we haven’t provided any annotation as we need to manually tell Hilt how to provide it to us. Now, you might already be used to it because we first need to tell Hilt to which container this class belongs to, thus how long should it keep it alive.
Because this is a repository, we might want to keep it alive forever (as long as the application lives) therefore we used the @Singleton
annotation and told Hilt that this class should be alive as long as the application is alive.
Also, as we need Hilt to inject this repository in the MessageViewModel, we have to make it injectable by adding the @Inject
constructor annotation. Getting back to the repository, it seems that it has two dependencies: a retrofit instance and storage provider.
Now, these dependencies are a bit trickier as we will be using modules in order to tell Hilt explicitly how to create them. First off, let’s start with the retrofit module class that we will annotate with @Module
:
We also told Hilt that this module should be part of the application container with the @InstallIn
annotation. This basically means that these provider methods can be accessed by any container component within the app (retrofit can be injected anywhere in the app).
Alright, so in this module we told Hilt how it should provide the retrofit instance globally by using the @Singleton
annotation, but isn’t it weird that we also told him how to provide the OkHttpClient
?
Well not so much because our retrofit instance generation needs an OkHttpClient in order to function and this is depicted very well by the fact that the method provideRetrofit
has such parameter. This is why we need to tell Hilt how Retrofit’s dependencies should be provided as well.
On to the final class that needs injection in the MessageRepository
, the ISharedStorage
dependency! Now, Hilt doesn’t know how to provide this interfaced (or indirected) dependency without us telling him, so let’s create a module just like we previously did for the Retrofit one:
The module has similar annotations as the class has @Module
as well as the @InstallIn
to specify the appropriate container component, but something is different, can you spot it ? This time, the dependency is indirected thus abstract. Following this, we can use a simpler annotation than @Provides
which is called @Binds
that allows us to define abstract dependencies (based on interfaces in our case).
For this to work though, we must inject the constructor of the class/es that implement the interface in cause: ISharedStorage
with the @Inject
annotation. Also, notice that we no longer specify the container component annotation because we already did this in the SharedStorageModule
(by adding the @Singleton
annotation):
The SharedStorage
class that implements our interface has pretty easy dependencies to work with, only an application context that we provide with the handy @ApplicationContext
annotation.
Perfect, we’re done! We have successfully created our first DI app! It’s true that in reality things are much more complicated, but this is a good starting point for someone trying out Dependency Injection in Android. Check out the full repository here!
We will cover in the upcoming articles more advanced annotations and we will provide better explanation to many of the concepts we have just uncovered here, but until next time, be well!
I want you focused so take a break, and see ya in the next article! And remember, if you enjoyed this article, you can buy me a coffee using the coffee bottom widget!
hello there, your site is very good.Following your articles.
Thank you! I appreciate it!
hello there, your article is amazing.Following your news.
Thanks a lot!
hi there, your style is so good.Following your posts.
I really appreciate it, thanks!
I get what you mean, but I want some detail. Is it ok if I PM you?
Hey, sure! Please do!
Sorry for the late response.
hi there, your style is perfect.Following your articles.
hi, your site is very good.Following your news.
hello, your post is so good.Following your articles.