Romman Sabbir
Romman Sabbir | Official

Romman Sabbir | Official

Dependency Injection (Android): Implementation (Hilt) [PART 2]

Dependency Injection (Android): Implementation (Hilt) [PART 2]

Romman Sabbir's photo
Romman Sabbir
·Aug 15, 2022·

5 min read

Subscribe to my newsletter and never miss my upcoming articles

Table of contents

  • Things to know
  • Keypoints
  • Installation of Hilt
  • First, Let's talk about @Annotations

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
    class MainActivity: BaseActivity(){
      @Inject
      lateinit val welcomeMessage : WelcomeMessage //Val not Var
      onCreate(){
      //Access the object here
      welcomeMessage.showGrettings()
      }
    }
    
    Also, allows you to inject 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 or Fragment, 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. Becuase ViewModel or LifeCycleComponents 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 or Context to any dependent object, it must be defined in specific module. In this case ActivityComponent.

@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 an Activity/Fragment and the Dialog object requires Context 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.

 
Share this