Dependency Injection (Android): Implementation (Hilt) [PART 2]
Things to know
Hilt is a wrapper to provides a standard way to incorporate Dagger dependency injection into an Android application. What kind of benefits Hilt provides?
- To simplify Dagger-related infrastructure for Android apps.
- To create a standard set of components and scopes to ease setup, readability/understanding, and code sharing between apps.
- To provide an easy way to provision different bindings to various build types (e.g. testing, debug, or release).
[ Ref : (Offical Doc) ]
So, Hilts use Dagger under the hood. Let's get introduced to Dagger.
Dagger is a fully static, compile-time dependency injection framework for Java, Kotlin, and Android.
Dagger automatically generates code that mimics the code you would otherwise have hand-written. Because the code is generated at compile time, it's traceable and more performant than other reflection-based solutions such as Guice
Benefits of Dagger
- Generating the AppContainer code (application graph) that you manually implemented in the manual DI section.
- Creating factories for the classes available in the application graph. This is how dependencies are satisfied internally.
- Deciding whether to reuse a dependency or create a new instance through the use of scopes.
- Builds and validates dependency graphs, ensuring that:
- Every object's dependencies can be satisfied, so there are no runtime exceptions.
- No dependency cycles exist, so there are no infinite loops.
- Generates the classes that are used at runtime to create the actual objects and their dependencies.
[ Ref : (Offical Doc) ]
Dagger Components
Dagger can create a graph of the dependencies in your project that it can use to find out where it should get those dependencies when they are needed. Dagger creates a container as you would have done with manual dependency injection.
Simple explaination is here - PART 1
Keypoints
- Hilt is wrapper of Dagger.
- Dagger is fully static, compile-time dependency injection framework.
- Dagger create components for you, where you can request for an dependent object.
- To create an dependent object for you, you need to provide a sort of way to Dagger to create that object.
- You can define an object lifecycle (ex. Singleton Object or Fresh Object)
- Ensure that object's dependencies can be satisfied and there are no runtime exceptions.
- Most Important, Saves your time and helps you to maintain a good system defined application.
Installation of Hilt
Add this block of code to the project's root build.gradle
which is known as hilt-android-gradle-plugin
classpath("com.google.dagger:hilt-android-gradle-plugin:2.38.1")
Now add those to app/build.gradle
to apply Gradle Plugin
and required dependencies
.
plugins {
kotlin("kapt")
id("dagger.hilt.android.plugin")
}
android {
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
}
dependencies {
implementation("com.google.dagger:hilt-android:2.38.1")
kapt("com.google.dagger:hilt-android-compiler:2.38.1")
}
// Allow references to generated code
kapt {
correctErrorTypes = true
}
First, Let's talk about @Annotations
Hilt expose some lovely annotations which helps a lot to minizime the whole Dagger stuff and their works is amazing.
@Inject
: Most common one.
This annotations is helps to inject a dependent object via Hilt. Field
object can be injected through DI by it's must be public, otherwise Hilt can't access the object for injection.
- Example
Also, allows you to injectclass MainActivity: BaseActivity(){ @Inject lateinit val welcomeMessage : WelcomeMessage //Val not Var onCreate(){ //Access the object here welcomeMessage.showGrettings() } }
contructor
and Hilt will provide all of the required objects through the constructor. - Example
class MainViewModel @Inject constructor( privale val welcomeMessageUseCase: WelcomeMessageUseCase ): ViewModel(){ init{ //access the object here scope.launch{ welcomeMessageUseCase.getGrettings() } } }
@HiltAndroidApp
:
Application
must be annotated with @HiltAndroidApp
to enable Hilt. Otherwise nothings gonna work at all. This is the entry point to create dependency container for the Application object's lifecycle and provides dependencies to it. It is the parent component of the app, which means that other components can access the dependencies that it provides.
- Example
@HiltAndroidApp //Super easy! class MySuperApplication : Application(){}
@AndroidEntryPoint
:
Hilt can provide dependencies to Android Specific Classes that annotated with @AndroidEntryPoint
. Android Clases are:
Application
,ViewModel
,Activity
,Fragment
,View
,Service
,BroadcastReceiver
.
- If you annotated a
View
orFragment
, you must also annotated the parent one, ex.Activity
.
Example:
@AndroidEntryPoint //Child is annotated
class MindBlowingFragment : BaseFragment(){}
@AndroidEntryPoint //Parent is annotated
class MainActivity: BaseActivity(){//activity contain a instance of MindBlowingFramgent}
- Now, if you want to provide any dependency to the Child or Parent Android Classes, Hilt can do it for you thorugh
Injection
(ex.@Inject lateinit val anObject : SomeObject
).
If you want to inject any object to
ViewModel
, you have to use@HiltViewModel
annotation. BecuaseViewModel
orLifeCycleComponents
comes with Android JetPack, not Android Platform itself.
@Module
:
- Define a Class to be the responsible for how to provide instances of certain types. Ex, you can't inject interface or abstract class, becuase you or Hilt can't instantiate those, you need a implementation. So, here you define which implementation to provide to the client as the type of interface or abstract class.
- Also, if you need to provide
ActivityContext
orContext
to any dependent object, it must be defined in specific module. In this caseActivityComponent
.
@InstallIn
:
To install module in which Components. All of the defined object initialization will be bounded to the
Component
lifecycle.Ex, If you inject a
Dialog
object to anActivity/Fragment
and theDialog
object requiresContext
in the constructor, it must be defined under a@ActivityComponent
lifecycle.- Components are, Injected for and lifetimes are:
SingletonComponent - Application - Lifetime: Application#onCreate() to Application destroyed
ViewModelComponent - ViewModel - Lifetime: ViewModel created to ViewModel destroyed
ActivityComponent - Activity - Lifetime: Activity#onCreate() to Activity#onDestroy()
FragmentComponent - Fragment - Lifetime: Fragment#onAttach() to Fragment#onDestroy()
ViewComponent - View - Lifetime: View#super() to View destroyed
ServiceComponent - Service - Lifetime: Service#onCreate() to Service#onDestroy()
That's it for today. In next part, we will jump into the detail & implementation of Hilt.