Basic Dependency Injection with Hilt!

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

  1. Setting up Hilt
  2. 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!

17+

11 thoughts on “Basic Dependency Injection with Hilt!

Comments are closed.

%d bloggers like this: