Logo

dev-resources.site

for different kinds of informations.

Dagger with a Hilt

Published at
7/24/2020
Categories
dagger
android
dependency
injection
Author
aniketsmk
Author
9 person written this
aniketsmk
open
Dagger with a Hilt

AndroidEntryPoint is now quite a bit better and in general availability so we can now move to this method!

This lets us specify the bare minimum to have a functional Dagger setup, with few drawbacks and get going really quickly with the code we care about.

Looking to rapidly bootstrap a new app with Dagger and Hilt?
Here are some steps to how it might practically be used. You could go in order to jump to the section you'd like.

Basic Setup

  1. Add classpath "com.google.dagger:hilt-android-gradle-plugin:2.28-alpha" to your project level build.gradle (the one at your project root)
  2. Add apply plugin: 'dagger.hilt.android.plugin' to the top of your app level build.gradle
  3. Ensure you have apply plugin: 'kotlin-kapt' in your app level build.gradle as well
  4. Import the following at the app level build.gradle:

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

Once you've gotten your setup ready, here's what you do.

Create an Application class

Then annotate it with @HiltAndroidApp

@HiltAndroidApp
class DemoApplication : Application()

Don't forget to add the classname of your application to the Manifest!

    <application
        android:name=".DemoApplication"
        android:allowBackup="true"

Let's get a ViewModel, but first..

Since it's only going to be useful with a repo and some Api passed in:

We're going to be using the standard separation of concerns which means we're going to need three classes

  1. The provider for the Retrofit instance
  2. The API interface for the network call to be made by retrofit
  3. The repository which will take in an instance of the created API and be responsible for exposing it to the ViewModel

We're going to create them in that order only because the app wouldn't compile in an intermediate state otherwise.

Creating a Retrofit Provider

I'll get to what @ApplicationComponent is and why the module is an object along with why this particular provider is marked @Singleton right after showing you code for this module.

import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.components.ApplicationComponent
import io.reactivex.schedulers.Schedulers
import okhttp3.OkHttpClient
import retrofit2.Retrofit
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
import javax.inject.Singleton

@InstallIn(ApplicationComponent::class)
@Module
object RetrofitModule {

    @Singleton
    @Provides
    fun getRetrofit(client: OkHttpClient) : Retrofit = Retrofit.Builder().baseUrl("https://something.com")
        .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
        .client(client)
        .build()

    @Provides
    fun getClient() : OkHttpClient = OkHttpClient()
}

Since we only ever want one instance of Retrofit (to improve connection pooling) this is a Singleton provider.
It might feel odd having a default okhttp client provided but this is often modified with interceptors.

This is installed in the Application component since we want the retrofit instance to not only be unique for the entire app, but also at the highest level in the dependency graph so it's available to all other modules.

This follows the standard practise of Modules being Singletons (via the object notation) for faster runtime performance.

Note: Since Dagger 2.25, we no longer need to annotate the functions JvmStatic.

Creating an API interface for the actual call

import io.reactivex.Observable
import retrofit2.http.GET

interface SomethingApi {
    @GET("widgets/")
    fun getWidgets(): Observable<String>
}

The pretend API has an endpoint called widgets and we're going to get ourselves some widgets!

Creating concrete instances of the API

Since it requires retrofit to create concrete instances of the API, we're going to need a module that will manage that.

import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.components.ActivityRetainedComponent
import retrofit2.Retrofit

@InstallIn(ActivityRetainedComponent::class)
@Module
object ApiModule {

    @Provides
    fun getSomethingApi(retrofit: Retrofit): SomethingApi =
        retrofit.create(SomethingApi::class.java)
}

Note that is in an ActivityRetainedComponent since it's intended to be used by a ViewModel that will itself be preserved on activity rotation. The only difference between the ActivityRetainedComponent and the ActivityComponent is that the provided object in the first scope is 'retained' or kept, through configuration changes. See here

Creating a Repository

This repo will abstract away the network and DB api's for the ViewModel


import io.reactivex.Observable
import javax.inject.Inject

class MainRepo @Inject constructor(private val somethingApi: SomethingApi) {
    fun getWidgets() : Observable<String> = somethingApi.getWidgets()
}

Finally the ViewModel

ViewModel Factories no longer need to be created since the Hilt compiler will do that bit of code generation.

You'll need implementation "androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha01" for the @ViewModelInject annotation.

Also kapt "androidx.hilt:hilt-compiler:1.0.0-alpha01" for the hilt compiler that will actually generate the ViewModel factories for you.

import androidx.hilt.lifecycle.ViewModelInject
import androidx.lifecycle.ViewModel

class MainViewModel @ViewModelInject constructor(
    private val mainRepo: MainRepo
) : ViewModel() {

}

Wrapping it up in the Activity

Now we're ready to use this ViewModel in the Activity and it's done with:

import android.os.Bundle
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import dagger.hilt.android.AndroidEntryPoint

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {

    private val mainViewModel: MainViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }
}

To get the viewModels delegate, you'll need to add the activity or fragment kotlin extension libraries depending on where you want to use it.
That's implementation "androidx.activity:activity-ktx:1.2.0-alpha06" or
implementation "androidx.fragment:fragment-ktx:1.3.0-alpha06"

That's it!
You're ready to use dependency injected viewmodels!

If you wanted to use this with fragments and want to share the activity's viewmodel with them, use activityViewModels instead of viewmodels in them.

Check back in later to see how to use this with navigation components and with testing.


Looking to get your devs up to speed on the latest techniques to boost productivity and your time to market? Reach out to me for consulting at [email protected]

dependency Article's
30 articles in total
Favicon
Ore: Advanced Dependency Injection Package for Go
Favicon
vcpkg - how to modify dependencies
Favicon
CocoaPods is in Maintenance Mode
Favicon
Safely restructure your codebase with Dependency Graphs
Favicon
Understanding Dependencies in Programming
Favicon
A zoom installer script for linux
Favicon
Loose Coupling and Dependency Injection (DI) principle
Favicon
Dependency Injection in swift
Favicon
Dependency relation in AWS CDK
Favicon
CORS how to enable them in .NET?
Favicon
Angular Dependency Injection
Favicon
Dependency management made easy with Dependabot and GitHub Actions
Favicon
Jetpack compose — Dependency injection with Dagger/HILT
Favicon
Dependencies in node project
Favicon
Fixing vulnerabilities found in a dependency tree
Favicon
How to create your own dependency injection framework in Java
Favicon
Reduzindo a quantidade de Branchs na criação de Objetos com uma estrutura plugável
Favicon
NodeJs - Dependency injection, make it easy
Favicon
A Step by Step Guide to ASP.NET Core Dependency Injection
Favicon
The Basics of Dependency Maintenance in NPM/yarn
Favicon
The troubles of modern software dependency management and what to do about them
Favicon
Loose Coupling Basics
Favicon
Correctly defining CDK dependencies in L3 constructs
Favicon
What dependency hell looks like, and how to avoid it
Favicon
How to create your own dependency injection framework in Java
Favicon
Dependency Inversion Principle in Swift
Favicon
Angular: Create a custom dependency injection
Favicon
Dagger with a Hilt
Favicon
How to find what is the dependency of a function, class, or variable in ES6 via AST
Favicon
Data Dependency Graph

Featured ones: