Like this beautiful clean building, we as android developers can build clean, fast and beautiful app architecture using many libraries like Hilt, Dagger …
Before we dive into the world of architecture and how to design a beautiful app architecture, let’s talk about dependency injection
Dependency Injection (DI) is a technique used widely in development in general and in android development in particular, the main idea behind DI is to inject all dependencies a class X needs rather than initializing them inside the class itself and by doing so you as developer gain lot of benefits
- Reusability of code
- Ease of refactoring the code
- Ease of testing
This where libraries like Dagger comes into the game.
We use Dagger to generate all needed dependencies between the classes so the developer can focus more on implementing the app functionalities more than focusing on how the classes will be linked.
What is Hilt ?
Hilt is the new jetpack library to implement dependency injection in your project and it’s recommended by the android team itself
in further sections we will discuss the integration of the library in your project, stay patient!
“If you don’t practice you don’t deserve to win” — Andre Agassi
What are we building ?
In order to discuss a good app architecture and the integrity of hilt, we will build a small quiz screen that shows a list of questions with their categories
For this we need list of dependencies: Hilt, Retrofit, Gson and Coroutine
All long this project we will be using MVVM architecture to structure our code
If you are not familiar with MVVM implementation don’t worry you just need some time to understand the idea behind it
In our case:
- View: contains the screen containing list of questions and categories
- ViewModel: the controller of our screen
- Repository: the class interacting with the Rest API and Retrofit interface
Integrate Hilt and Retrofit
- Add classpath to root level build.gradle
- Add plugin and dependencies in app level build.gradle
Create dependency core
In order to create injectable classes and interfaces, we need to create the core of dependency injection
In my point of view in Hilt the core is defined in two classes
- App class: defined with @HiltAndroidApp annotation
- App Module object: defined with @Module and @InstallIn annotations
so first we create a class named App as the following
And then we create another file contains the app module component in where we will define every injectable class and how it will be provided
The @InstallIn annotation is the Hilt way to define where the module will be installed, without this annotation the module will not be compiled and it will lead to compilation errors as mentioned in the official documentation
Define the API Interface
The next step is to define the api interface that will be used in the app to extract data from the server
In this example we will use this question db api, for a simple example our interface will contain only one function that gets a list of random 10 questions
don’t panic QuestionResponse is a class that I’ve created you will find it in the project source code we are just focusing here in the app architecture and dependency injection
After we created the interface we need to define how classes can use it’s functions for this we will add some code in the module object
- first we will define how OkHttp object is generated
- secondly we will define how retrofit will be generated
- and finally we provide the Api interface component
for every component we will create a provider function that will return the type that we need (OkHttp, Retrofit…) and to make those functions special we need just to add the @Provides annotation
So as you see the first function provide an OkHttpClient Object, the second provide a Retrofit object and the third define how IQuizApi will be referenced
but as you see also there’s @Singleton annotation above each function
@Singleton means that the object provided will be instantiated only once in the app lifecycle, for example here Retrofit will be generated only once and each time we need Retrofit reference we can get it.
Another note is the parameters in those functions (2nd and 3rd) those parameters will be provided automatically by Hilt once the code is compiled and that’s where all the magic happens so you don’t need to bother about all the dependency between the components
Create the api repository
To finish integrating the API in our project one last step is to define the Repository that will call the interface’s functions “getRandomQuestions”
Repository is an MVVM term refers to every class that make a direct call to data whether it’s in a local database (sqlite, room..) or distance server (api)
in the code above we’ve just created a Class QuestionRepository with a parameter type of IQuizApi, with suspend function “getQuestion” that makes call to getRandomQuestion in the api interface nothing fancy
but the magic here is the @Inject annotation we add this annotation so Hilt will provide the required parameters (IQuizApi in this example) so we don’t need to pass it when we use this Repository, and that’s the second trick done by Hilt!
ViewModel and Link it to Activity
The last step in this simple example is to create our ViewModel and View (Activity)
The ViewModel will be charged by calling the Repository class and getting the data from it, and the view will get the result from the ViewModel to display it
don’t panic if you are seeing advanced coroutine terms like “livdeData”, “Dispatchers” and “emit” I highly recommend you take a look in this link to fully understand the world of coroutine in this article we are focusing on Hilt and App architecture
So in this ViewModel we just created a simple class with one parameter in the constructor and one function
but as always all the magic is in the annotations we add
here @ViewModelInject is our superstar like@Inject, this annotation tells Hilt to provide all the needed requirements to our class in order to execute correctly, so with ViewModels we use @ViewModelInject
And in the last brick we create our View (Activity) to show the result as a list of questions.
The main parts in this code which you need to focus on are:
- @AndroidEntryPoint this annotation is part of Hilt framework, it start enabling members injection in your other Android classes, this annotation can be used with Activity, Fragment, Service, View or BroadcastReceiver
- by Viewmodel<QuizVM> is the new way to initialize your VM without using ViewModelProvider or other factory
As you see in this example the unbelievable small number of code lines we wrote to create a simple app that call a webservice and parse data to list of objects and then display it in a recyclerview
And also keep in mind that this sample is scalable and we can add as many features as we want with little time compared to using other frameworks like Dagger
So to conclude our discussion, all we need is to highlight the speed Hilt gave us to build scalable apps with the minimum number of code lines we can
In this example we take a tour on Hilt main components
- How to provide the type you want
- Inject a repository (any class)
- Inject view models
- Create an Android Entry point and an easy way how to initialize viewmodel
You can find all the source code of this example in the link below
Contribute to koukibadr/HiltExample development by creating an account on GitHub.
I hope you have learnt something new with this article.
Happy coding !