Dependency Injection with Hilt in Android - Part 2

·

3 min read

It is a continuous part of our previous article about Dependency Injection with Hilt. Please check it if you haven't yet.

Let's see some common 'How to' in Hilt in this article.

How to field-inject dependencies in non-Android classes?

As we know, To field-inject a dependency, we use @Inject annotation but it supports only Android classes by default with a predefined entry point @AndroidEntryPoint.

To field-inject a dependency in non-android classes (not supported by @AndroidEntryPoint), we must create a new entry point and access the dependencies through it.

Example

class Developer() {
    // We gonna inject for this
    private val cup = Cup()

    fun drinkCoffee() {
        // further steps to drink coffee
    }
}

class Cup() {
    // A cup
}

As you can see from the example, we gonna field-inject Cup instance into the Developer class.

First, define a new entry point which provides the Cup instance.

import dagger.hilt.EntryPoint
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent

@EntryPoint
@InstallIn(SingletonComponent::class)
interface CupEntryPoint {
    var cup: Cup
}

Next, access the cup instance from the entry point in the Developer class.

import dagger.hilt.android.EntryPointAccessors

class Developer {

    // Get the entry point
    private val entryPoint = EntryPointAccessors.fromApplication(
        applicationContext,
        CupEntryPoint::class.java
    )
    // Get the instance
    private val cup = entryPoint.cup

    fun drinkCoffee() {
        // further steps to drink coffee
    }
}

First, get the entry point using the respective method from the EntryPointAccessors.

Here we have used fromApplication() since the CupEntryPoint was created as a singleton component. You can use other methods like fromActivity() based on your needs.

After that, we accessed the dependency via the entry point. Simple.

How to provide multiple instances of the same type in Hilt?

In some cases, you may need to inject multiple instances of the same type into a class. In this scenario, you have to differentiate your hilt bindings with Qualifiers.

Example

class Developer @Inject constructor(
    private val coffeeCup: Cup,
    private val teaCup: Cup
) {

    fun drinkCoffee() {
        // further steps to drink coffee
        coffeeCup.useHere()
    }

    fun drinkTea() {
        // further steps to drink tea
        teaCup.useHere()
    }
}

class Cup() {
    // A cup
}

From the above example, you may guess the requirements. The Developer class needs two Cup instances i.e one to drink the coffee and another one to drink the tea.

By default, Hilt doesn't about how to differentiate the bindings when you provide the two instances of the Cup. So we have to use Qualifiers to let the hilt know which one is for coffee and which one is for tea.

@Qualifier
annotation class CoffeeCup

@Qualifier
annotation class TeaCup

@Module
@InstallIn(SingletonComponent::class)
object DeveloperModule {

   @Provides
   @CoffeeCup
   fun provideCoffeeCup(): Cup {
     // Your way to create the coffee cup
     // For simplicity
     return Cup()
   }

   @Provides
   @TeaCup
   fun provideTeaCup(): Cup {
     // Your way to create the tea cup.
     // For simplicity
     return Cup()
   }

   @Provides
   fun provideDeveloper(
        @CoffeeCup coffeeCup: Cup,
        @TeaCup teaCup: Cup
   ): Developer {
        return Developer(coffeeCup, teaCup)
   }
}

I have created Two annotation classes - CoffeeCup and TeaCup with annotation @Qualifiers.

Then use the qualifiers to differentiate the instances. Simple.

Note: Here I have used the Cup class with no difference and no constructor parameters for the sake of simplicity. Anyway the same way, you can provide bindings as per your needs easily.

I hope you learned something from this article.

Thanks for reading. <3.

Did you find this article valuable?

Support Dhina17 by becoming a sponsor. Any amount is appreciated!