A Hilt Tutorial for Android Dependency Injection First-Timers

Pius Aboyi
4 min readSep 2, 2020

Once you are done with understanding the basics of Android development and after building your first App, the next important thing should be writing good quality code. You can write better code and apps by following best practises and avoiding anti-patterns.

You might have heard about Dependency Injection (DI) in software development or a more Android-specific DI term like the almighty Dagger. If you haven’t heard about DI or dagger, this post is still for you. It took a few months from the first time I heard about Dagger to my first attempt to learn it. And guess what? I dreaded Dagger and would usually settle for manual DI on my personal projects. In the post, I will be attempting to teach Hilt to developers that have no prior experience using dagger or any other DI tool and developers that tried Dagger and found it scary.

What is Hilt

Hilt is simply a new tool for dependency injection in Android that simplifies the process of using Dagger. Hilt takes away much of the boilerplate codes associated with Dagger by Auto-generating dagger components and code.

Getting to the Main Gist

Let’s make hilt the only term we will define and write some code or talk about code ;).

The App I will be using in the post to explain Hilt is a PDF reader I built using the MVVM architecture. The app has a ROOM database and uses LiveData and ViewModel. You can find full source code for the App here.

1. Adding Hilt to Project

Open the project level build.gradle file in Android Studio and add the following line of code under the dependency block:

classpath "com.google.dagger:hilt-android-gradle-plugin:2.28-alpha"

Next, open the app-level build.gradle and make the following changes:

  1. Below apply plugin: 'kotlin-kapt', add the following line of code:

apply plugin: 'dagger.hilt.android.plugin'

2. Add the following code in the dependency block:

implementation "com.google.dagger:hilt-android:2.28-alpha"
kapt "com.google.dagger:hilt-android-compiler:2.28-alpha"

3. And lastly, still, inside the dependency block, add hilt’s androidx compiler.

kapt 'androidx.hilt:hilt-compiler:1.0.0-alpha01'

And with that, sync Gradle. Your project is ready to use hilt. In the next section, we will start writing code to perform dependency injection using hilt.

2. Create Application Class

At this point, we will be creating a new class that extends Application. This will serve as the entry point for our App.

Remember how MainActivity.java/kt file is the default entry point of your most apps? This is almost the same thing. However, the class we will be creating does not extend Activity and we won't be coding our first screen inside it. Now, let's see all that in code :D.

Create a new class and name it App.kt. Add the following code to the new class:

@HiltAndroidApp
class App : Application()

The key thing in this file is the @HiltAndroidApp annotation.

3. Update Manifest File

Open AndroidManifest.xml file and register the new App.kt class you just created in the last step. You can do this by adding android:name=”.App” under the <application>. See example below:

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:name=".App"
...

4. Create Hilt Module

So far, we have added Hilt to our app and have configured the app to start up from the App class we created in the previous step. Now, let's create our first DI module. For neatness purpose, I made a dedicated di package for the DI module (DatabaseModule.kt).

Our module for the example PDF reader app provides the following dependencies:

  1. AppDatabase
  2. DocumentDao

Let’s take a look at the content DatabaseModule.kt DI module.

@Module
@InstallIn(ApplicationComponent::class)
class DatabaseModule {

@Provides
@Singleton
fun provideDatabase(@ApplicationContext appContext: Context): AppDatabase {
return Room.databaseBuilder(
appContext,
AppDatabase::class.java,
"wise_pdf_db"
).fallbackToDestructiveMigration()
.build()
}

@Provides
fun provideDocumentDao(db: AppDatabase): DocumentDao {
return db.documentDao()
}


}

First, the class is annotated with @Module and @InstallIn(). Second thing, we have methods that return each dependency annotated with @Provides and @Singleton (In the case of AppDataBase).

5. Providing Dependencies

Let's try to paint a picture of how classes in our PDF reader depend on each other.

First, the MainActivity class depends on MainActivityViewModel class which depends on DocumentRepository. DocumentRepository also depends on DocumentDao which takes the dependency relationship to our Room Database.

MainActivity.kt -> MainActivityViewModel.kt -> DocumentRepository.kt

DocumentDao.kt ->AppDatabase.

From our DatabaseModule class, hilt already knows how to provide DocumentDao and AppDatabase.

In order to provide MainActivityViewModel, annotate the constructor of MainActivityViewModel class with @ViewModelInject.

class MainActivityViewModel @ViewModelInject constructor(
private val documentRepository: DocumentRepository) : ViewModel() {
val recentDocuments: LiveData<List<Document>> = documentRepository.recentDocuments


fun
insert(document: Document) = viewModelScope.launch(Dispatchers.IO) {
documentRepository
.insert(document)
}
}

Similarly, annotate the constructor of DocumentRepository with @Inject so hilt knows how to provide the class.

class DocumentRepository @Inject constructor(private val documentDao: DocumentDao) {

val recentDocuments: LiveData<List<Document>> = documentDao.getRecentDocs()

suspend fun insert(document: Document) {
documentDao.insert(document)
}
}

6. Use Hilt in Activity

At this point, we are ready to start using Hilt in our activity. Open MainActivity.kt and annotate the class with @AndroidEntryPoint.

Next, create a field of type MainActivityViewModel using “by viewModels()”.

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {

val viewModel: MainActivityViewModel by viewModels()
...
}

Note: viewModel() imports androidx.activity. Make sure to include the dependency in your app-level build.gradle.

7. No Next Step

Yeah! That’s all.🎉 At this point, you can run your app and see that it works without you manually providing dependencies in your code.

Dependency Injection can be a big deal and a bit complex. But a neat technic for writing high-quality code. I have attempted to demonstrate how to set up DI using Hilt with an MVVM project which uses a Room database. Hilt goes deeper than the scope of this article. I recommend you take a look a the official Hilt codelab and the Hilt documentation to discover other cool stuff about Hilt not covered in this post. If you have any questions or recommendation, feel free to connect with me on twitter via @ea_pius. Peace and Happy coding.

--

--

Pius Aboyi

Web/Mobile Developer(Android), I know PHP, SEO+Digital marketing. Currently growing the tech Eco-system in Benue, Nigeria, via BenueTechForum and GDG Makurdi.