Skip to content

Dependency Injection with Hilt in Android

Published: at 8 min read

Simplify the Word - Dependency Injection

Goals of using Dependency Injection

  1. Decoupling: Helps in reducing the coupling between different components of a system. Components are no longer responsible for creating their own dependencies, causing them more reusable and easier to maintain.

  2. Testability: Easier to write unit tests for the code. One can inject mock or test-specific dependencies when testing a component, allowing to isolate and control the behavior of the component.

  3. Flexibility: Enables to change the behavior of a component by simply changing the injected dependencies, rather than modifying the component’s code making the codebase more adaptable to changes and extensions.

Dependency Injection in Android

Some popular Dependency Injection frameworks for Android include Dagger, Hilt, and Koin.

These frameworks help to manage the creation and injection of dependencies into the Android components like Activities, Fragments, Services, and ViewModels.

Overview of how DI works in Android

Hilt for Dependency Injection

Why use Hilt for Dependency

What is Qualifier in Hilt Dagger

@Module
@InstallIn(SingletonComponent.class)
public class MLoggingModule {
    @Provides
    @ConsoleLoggerQualifier
    public MLogger provideConsoleLogger() {
        return new ConsoleLogger();
    }

    @Provides
    @FileLoggerQualifier
    public MLogger provideFileLogger() {
        return new FileLogger();
    }
}

What is Annotation in Hilt Dagger

@HiltAndroidApp
public class MainApplication extends Application {
    // ...
}

Important annotations used in Hilt

  1. @HiltAndroidApp ~

    Used to mark the custom Application class, indicating that Hilt should initialize itself for the Android application. It’s typically the entry point for setting up Hilt in the app.

@HiltAndroidApp
public class MainApplication extends Application {
    // ...
}
  1. @AndroidEntryPoint ~

    When we annotate an Android component(like Activity, Fragment) with @AndroidEntryPoint, Hilt will handle the injection of dependencies into that component.

@AndroidEntryPoint
public class MainActivity extends AppCompatActivity {
    // ...
}
  1. @Module ~

    We use @Module to define Dagger modules where we provide bindings for the dependencies. These modules are then installed in Dagger components using the @InstallIn annotation.

@Module
@InstallIn(ActivityComponent.class)
public class MainModule {
    // ...
}
  1. @InstallIn ~

    This annotation specifies which Dagger component a module should be installed in. For example, one can use @InstallIn(ActivityComponent.class) to indicate that a module should be available for injection in activities.

  2. @Provides ~

    Within a Dagger module, we use @Provides to define methods that provide instances of dependencies. These methods are responsible for creating and returning the required objects.

@Module
@InstallIn(ActivityComponent.class)
public class MainModule {
    @Provides
    public ApiService provideApiService() {
        return new ApiService();
    }
}
  1. @Inject ~

    In Android components (e.g., activities, fragments, view models), we use @Inject to indicate that a field, constructor, or method requires dependency injection. Hilt will inject the requested dependencies into these marked elements.

@AndroidEntryPoint
public class MainActivity extends AppCompatActivity {
    @Inject
    ApiService apiService;
    // ...
}
  1. @ViewModelInject ~

    For injecting dependencies into Android ViewModel classes, one can use @ViewModelInject. This annotation allows Hilt to inject dependencies into the ViewModel’s constructor.

@HiltViewModel
public class MainViewModel @ViewModelInject constructor(
    private final MainRepository repository
) : ViewModel() {
    // ...
}
  1. @Binds ~

    This annotation tells Hilt which implementation to use when it needs to provide an instance of an interface.

Extra Facts about Hilt

Component lifetimes - Hilt automatically creates and destroys instances of generated component classes following the lifecycle of the corresponding Android classes.

Hilt automatically generates and provides the following -

Hilt currently supports the following Android classes:

  1. Application (by using @HiltAndroidApp)
  2. ViewModel (by using @HiltViewModel)
  3. Activity
  4. Fragment
  5. View
  6. Service
  7. BroadcastReceiver

Code for adding Hilt in Android

Step 1 : In project/build.gradle file, please add

    plugins {
        id("com.google.dagger.hilt.android") version "2.44" apply false
    }

Step 2 : In app/build.gradle file, please add

plugins {
  kotlin("kapt")
  id("com.google.dagger.hilt.android")
}

android {
  ...
}

dependencies {
  implementation("com.google.dagger:hilt-android:2.44")
  kapt("com.google.dagger:hilt-android-compiler:2.44")
}

kapt {
  correctErrorTypes = true
}

Step 3 : Hilt uses Java 8 features. To enable Java 8 in your project, add the following to the app/build.gradle file:

android {
  ...
  compileOptions {
    sourceCompatibility = JavaVersion.VERSION_1_8
    targetCompatibility = JavaVersion.VERSION_1_8
  }
}

Step 4 : @HiltAndroidApp triggers Hilt’s code generation, including a base class for the application that serves as the application-level dependency container.

@HiltAndroidApp
class MainApplication : Application() {
}

Step 5 : In AndroidManifest.xml file add the below code under tag

android:name=".MainApplication"

Applying the above Lets do some coding of fetching some api data and showcase

Step 6 : Creating a data class - FactModel

data class FactModel(val fact: String)

Step 7 : Creating an interface

interface FactsApi {
    @GET(FACTS_ENDPOINT)
    suspend fun getFacts(): Response<List<FactModel>>
}

Step 8:

class FactService  @Inject constructor(private val factsApi: FactsApi){
    suspend fun getFacts(): List<FactModel>{
        return withContext(Dispatchers.IO){
            val facts = factsApi.getFacts()
            facts.body()?: emptyList()
        }
    }
}

Step 9 :

class FactsRepository @Inject constructor(private val factService: FactService){
    suspend fun getFacts(): List<FactItem>{
        return factService.getFacts().map {
            it.toFactItem()
        }
    }
}

Step 10 :

class GetFactsUseCases @Inject constructor(private val factsRepository: FactsRepository){
    suspend operator fun invoke(): List<FactItem>{
        return factsRepository.getFacts().shuffled()
    }
}

Step 11 :

@HiltViewModel
class FactViewModel @Inject constructor(private val factsUseCases: GetFactsUseCases):ViewModel(){
    private val _facts = MutableStateFlow(emptyList<FactItem>())
    val facts : StateFlow<List<FactItem>> get() = _facts

    init {
        getFacts()
    }

    private fun getFacts() {
        viewModelScope.launch{
            try{
                val fact = factsUseCases()
                _facts.value = fact
            }catch (_: Exception){}
        }
    }
}

Step 12 : Here we use compose to get the fact data from viewmodel, and in fragment/ activity class

@AndroidEntryPoint
class SplashScreenFragment : Fragment() {
    private val splashViewModel: SplashViewModel by viewModels()

val fact = splashViewModel.facts.collectAsState()
}

Drawbacks of Hilt

Subject to note that - It’s important to note that the disadvantages of Hilt should be considered in the context of the specific project requirements and team expertise. Hilt can be an excellent choice for many Android projects, especially those where simplicity and Android-specific features are valued.

Summary

By adding Hilt dependencies, we implement dependency injection in Android, Which make the code more modular, testable, and maintainable, while also promoting better separation of concerns within the application.

Share :
Written by:Parita Dey

Interested in Writing Blogs, showcase yourself ?

If you're passionate about technology and have insights to share, we'd love to hear from you! Fill out the form below to express your interest in writing technical blogs for us.

If you notice any issues in this blog post or have suggestions, please contact the author directly or send an email to hi@asdevs.dev.