Introduction to Dependency Injection
When a class wants a reference to another class that is referred to as Dependency. An example is, a Dog class may need a reference to an Animal class to get some of its properties.
Manual Dependency Injection Example:
//Animal class
class Animal(){
//eat function
fun eat(){
println("Animals Eat For Growth and development")
}
}
//Dog class
class Dog(private val animal:Animal){
//eat function
fun eat(){
//animal instance invoking eat function
animal.eat()
}
}
//main fuction
fun main(){
val animal = Animal()
val dog = Dog(animal)
dog.eat()
}
The main function uses an instance of the Dog class to call the eat function. The Dog class depends on the Animal class, the app creates an instance of the Animal and then uses it to construct an instance of Dog then calls the eat function from the Dog class which returns the details of the eat function in the Animal class.
Output
Animals Eat For Growth and development
The output of the main function shows the main function uses the Dog class because the Dog class depends on the Animal class.
The above method of Dependency injection is referred to as Constructor Injection as you pass the dependency of a class to its Constructor.
Automated Dependency Injection
The above example is a manual dependency injection since we have created, provided and managed the dependency of different classes by hand without a library. But once the App starts to scale you require more dependencies which will be more tedious creating and managing the dependencies manually hence you require libraries like Dagger and Hilt which rides on Dagger to Automatically create and provide Dependencies
Why Dependency Injection with Hilt?
Hilt is a jetpack-recommended library for dependency injection in android that defines a standard way to implement DI by providing containers for every android class in your android project and maintaining their lifecycle automatically. Hilt is built on top of Dagger library to benefit from Compile time correctness, Runtime performance, Scalability and Android Support that Dagger provides.
Setting Up Hilt Dependencies.
1. Add the hilt-android-Gradle-plugin to your project's root build.Gradle file
plugins {
...
id("com.google.dagger.hilt.android") version "2.44" apply false
}
2 Apply the Gradle plugin and add these dependencies to your app/build.Gradle file
plugins {
kotlin("kapt")
id("com.google.dagger.hilt.android")
}
dependencies {
.......
implementation("com.google.dagger:hilt-android:2.44")
kapt("com.google.dagger:hilt-android-compiler:2.44")
}
Hilt Components.
All apps that have hilt must have an application class that is annotated with @HiltAndroidApp
. @HiltAndroidApp
triggers hilt code generation by including a base class for your application that's servers as an application-level dependency container.
The generated Hilt component is attached to the Application object's lifecycle and provides dependencies to it. Since the Application class is the parent component of the App, other classes can access the dependencies that it provides.
@HiltAndroidApp
class PlusApplication:Application() {
override fun onCreate() {
super.onCreate()
initTimber()
}
private fun initTimber(){
Timber.plant(Timber.DebugTree())
}
}
Example Of Hilt Dependency Injection.
Once you have setup Hilt in your application class and the application-level component is available then Hilt will provide dependencies to other android classes that have the @AndroidEntryPoint
annotation.
If you annotate an Android class with @AndroidEntryPoint
, then you also must annotate its sub-classes or classes that depend on the annotated class. For example, if you annotate a fragment with @AndroidEntryPoint
, then you must also annotate any other activities or fragments where the annotated fragment is used.
@AndroidEntryPoint
generates an individual Hilt component for each Android class in your project:
@AndroidEntryPoint
class SignInActivity : AppCompatActivity() { ...... }
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.
How to Obtain dependencies?
To obtain dependency from a component, use the @Inject
annotation to perform a field injection:
@HiltViewModel
class SignInViewModel @Inject constructor(
@ApplicationContext context: Context
) : ViewModel(){ ....... }
AOB
Classes that Hilt inject can have other base classes that also use injection. Those classes don't need the @AndroidEntryPoint annotation if they're abstract.
Conclusion.
With the above knowledge on Dependency injection with Hilt you are ready to start implementing it on your android projects. Dependency injection is a good technique for creating scalable and testable Android apps. Some of the advantages of using automated Dependency injections with hilt are:
Reusability of classes and decoupling of dependencies: It's easier to swap out
Ease of refactoring: The dependencies become a verifiable part of the API so they can be checked at the object-creation time or at compile time rather than being hidden.
Ease of testing: A class doesn't manage its dependencies, so when you're testing it.