Hilt is a Dependency injection library for Android. It reduces manual dependencies in projects. Hilt is developed on a Dagger basis, providing containers for common Android classes and automatically managing their life cycles, among other things.

The concepts in the document are too vague, so there is no intuitive feeling about the differences between Hilt and Dagger in the use. The purpose of this paper is to analyze the differences between Hilt and Dagger in detail.

In the previous two articles, we have introduced the meaning and usage of Hilt annotations in detail, and attached detailed cases. There are detailed comments in the code. To save space, this article will not introduce the meaning of Hilt annotations in detail.

  • Introduction to common Hilt annotations, some of the pitfalls encountered during the implementation process, how Hilt binds to Android framework classes, and their lifecycle.
  • How to use annotations in ViewModel, App Startup, ContentProvider, etc.
  • The code has been uploaded to GitHub: AndroidX-Jetpack-Practice if it helps you, please give me a like in the upper right corner of the warehouse, thank you.

In the previous article, I abandoned Dagger hug Koin and analyzed the differences between Dagger and Koin in compilation time and use, etc. This article mainly analyzes the differences between Hilt and Dagger from the following aspects.

  • Initialization comparison?
  • Compare to Android framework classes?
  • Compared with Room and WorkManager?
  • Compare with ViewModule?
  • Limitations of Hilt in multiple modules?

Initialization comparison

With either Hilt or Dagger, you need to initialize the Dagger inside the Application, which is the entry point to the dependency injection container.

Dagger

Dagger we have to create the corresponding file with the @Module and @Component annotations and inject the Application

// Component declares all modules // ActivityAllModule configates all activities @singleton@component (modules = arrayOf( AndroidInjectionModule::class, ActivitylModule::class)) interface AppCompoment { fun inject(app: App) @Component.Builder interface Builder { @BindsInstance funbindApplication(app: Application): Builder
        fun build(): AppCompoment
    }

}
Copy the code

After creating the Modules and Components files, you need to initialize the Dagger in the Application. You need to implement the HasActivityInjector interface to automatically manage the Activity.

class App : Application(), HasActivityInjector {

    @Inject
    lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Activity>

    override fun attachBaseContext(base: Context?) {
        super.attachBaseContext(base)
    }

    override fun onCreate() {
        super.onCreate()
         DaggerAppCompoment.builder()
                .bindApplication(this)
                .build()
                .inject(this)
    }

    override fun activityInjector(): AndroidInjector<Activity> {
        return dispatchingAndroidInjector
    }
}
Copy the code

Hilt

Instead of manually specifying the inclusion of each module in Hilt, adding the @HILtAndroidApp annotation to the Application will trigger the generation of Hilt code that will be used as the base class for the Application dependency container.

@HiltAndroidApp
class HiltApplication : Application() {/** * 1. All apps using Hilt must contain an Application annotated by @hiltAndroidApp * 2. Includes base classes used as application dependency containers * 3. The generated Hilt component is attached to the Application lifecycle and is also the parent of the App, providing a dependency for other components to access * 4. After setting up @hiltAndroidApp in your Application, you can use the components provided by Hilt. The @androidEntryPoint annotation provided by Hilt is used to provide Android class dependencies (Activity, Fragment, View, Service, BroadcastReceiver) for Application use @hiltAndroidapp Annotation */}Copy the code

All apps that use Hilt must contain an Application annotated with @hiltAndroidApp, which is the entry point to the dependency injection container.

Hilt provides two predefined qualifiers, @ApplicationContext and @ActivityContext, that you can use directly without requiring the developer to inject the Application himself.

Compare with the Android framework class

Dagger and Hilt support for the most common Android classes Application, Activity, Fragment, View, Service, BroadcastReceiver, Let’s take activities and fragments as examples.

Dagger

Every Activity and Fragment in the Dagger needs to be told how to inject the Dagger, so we need to create the corresponding ActivityModule, FragmentModule.

Every time a new Fragment and the Activity must be added in the corresponding Module file, when added to the Activity every time, you need to add @ ContributesAndroidInjector annotations, used to automatically generate the relevant code component, To help reduce duplicate template code, a class called ActivitylModule_ContributeXXXXActivity is automatically created at compile time to help generate the injected code.

// Put all activities into ActivitylModule to manage @Module abstract classActivitylModule() {/ / ContributesAndroidInjector used to automatically generate sub-components related code, help us to reduce duplication of template code / / modules specified child components (current MainActivity contains two fragments, // Each new activity needs to be manually added here. // Specify the activity lifecycle @activityScope with the annotation @activityScope @ContributesAndroidInjector(modules = arrayOf(FragmentModule::class)) abstract fun contributeMainActivity():MainActivity } / / manage all fragments @ Module abstract class FragmentModule {/ / if the current has a new fragments @ ContributesAndroidInjector need to add to this Module  abstract fun contributeHomeFragment(): HomeFragment @ContributesAndroidInjector abstract fun contributeAboutFragment(): AboutFragment }Copy the code

Dagger provides HasSupportFragmentInjector interface to automatically manage fragments, all of the Activity inheritance BaseActivity, we need to implement HasSupportFragmentInjector, Add AndroidInjection.inject(this) to activities and fragments.

abstract class BaseActivity : AppCompatActivity(),HasSupportFragmentInjector {

    @Inject
    lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Fragment>
        override fun onCreate(savedInstanceState: Bundle?) {
        AndroidInjection.inject(this)
        super.onCreate(savedInstanceState)
    }
    
    override fun supportFragmentInjector(): AndroidInjector<Fragment> {
        return dispatchingAndroidInjector
    }
}

abstract class BaseFragment : Fragment() {
    override fun onAttach(context: Context?) {
        super.onAttach(context)
        AndroidSupportInjection.inject(this)
    }
}
Copy the code

Hilt

In Hilt, the Android framework classes are completely managed by Hilt for me. We just add @AndroidEntryPoint to Activiyt and Fragmetn.

@AndroidEntryPoint
class HitAppCompatActivity : AppCompatActivity() {

}

@AndroidEntryPoint
class HiltFragment : Fragment() {}Copy the code

Hilt really does a lot of optimization, compared to the Dagger, a lot of template code is removed, no need for manual management, developers just need to worry about how to bind.

Compare with Room and WorkManager

Now let’s look at the difference between Dagger and Hilt for Room and WorkManager. Let’s use Room as an example.

Dagger

Using Room in the Dagger requires creating the RoomModule file using the @Module annotation and then adding the RoomModule to the Component.

@Module
class RoomModule(val app: Application) {

    @Provides
    @Singleton
    fun providerAppDataBase(): AppDataBase = Room
            .databaseBuilder(app, AppDataBase::class.java, "dhl.db"). FallbackToDestructiveMigration (). AllowMainThreadQueries (). The build ()} / / Component declares all the modules / / ActivityAllModule is configured with all activities // RoomModule and database-related @singleton@component (modules = arrayOf( AndroidInjectionModule::class, RoomModule::class, ActivitylModule::class)) interface AppCompoment { fun inject(app: App) @Component.Builder interface Builder { @BindsInstance funbindApplication(app: Application): Builder
        fun build(): AppCompoment
    }

}
Copy the code

The Dagger needs to add the component’s corresponding life cycle @singleton, @ActivityScope, and so on to the corresponding module.

  • @SingletonCorresponding Application.
  • @ActivityScopeCorresponding Activity.

Hilt

In Hilt we just need to create RoomModule files using the @Module annotation. We don’t need to add modules manually.

Using the @ InstallIn annotations to specify the module of life cycle, such as using the @ InstallIn (ApplicationComponent: : class) annotation module will be bound to the Application of life cycle.

@ the Module @ InstallIn (ApplicationComponent: : class) / / used ApplicationComponent here, so RoomModule bound to the Application of life cycle. Object RoomModule {/** * @provides is used to annotate the internal methods of a class by @Module and provide dependency objects. * @singleton Provides @singleton fun provideAppDataBase(Application: Application): AppDataBase {return Room
            .databaseBuilder(application, AppDataBase::class.java, "dhl.db")
            .fallbackToDestructiveMigration()
            .allowMainThreadQueries()
            .build()
    }
}
Copy the code

Hilt provides the following components to bind dependencies to corresponding Android classes.

Components provided by Hilt Corresponding Android class
ApplicationComponent Application
ActivityRetainedComponent ViewModel
ActivityComponent Activity
FragmentComponent Fragment
ViewComponent View
ViewWithFragmentComponent View annotated with @WithFragmentBindings
ServiceComponent Service

Compare with ViewModule?

We inject a ViewModels instance into the Android component and bind the ViewModels instance to the ViewModelFactory. The traditional call is as follows:

ViewModelProviders.of(this).get(DetailViewModel::class.java)
Copy the code

Let’s take a look at using ViewModule for Dagger and Hilt.

Dagger

Within Dagger, for each ViewModel, we need to tell Dagger how to inject them, so we need to create a ViewModelModule file and manage all viewModels within ViewModelModule.

@Module
abstract class ViewModelModule {

    @Binds
    @IntoMap
    @ViewModelKey(DetailViewModel::class)
    abstract fun bindDetailViewModel(viewModel: DetailViewModel): ViewModel

    @Binds
    abstract fun bindViewModelFactory(factory: ViewModelFactory): ViewModelProvider.Factory
}
Copy the code

After creating the ViewModelModule file, you need to add the ViewModelModule to Component

// Component declares all modules // RoomModule and database-related // ActivityAllModule configates all activities // ViewModelModule configates all ViewModel @Singleton @Component(modules = arrayOf( AndroidInjectionModule::class, RoomModule::class, ViewModelModule::class, ActivitylModule::class)) interface AppCompoment { fun inject(app: App) @Component.Builder interface Builder { @BindsInstance funbindApplication(app: Application): Builder
        fun build(): AppCompoment
    }

}
Copy the code

Hilt

Hilt provides the @ViewModelInject annotation to inject ViewModel instances, and Hilt provides the @Assisted annotation to the SavedStateHandle class to inject SavedStateHandle instances.

class HiltViewModel @ViewModelInject constructor( private val tasksRepository: Repository, //SavedStateHandle Stores and recovers data when a process is terminated @assisted Private Val SavedStateHandle: SavedStateHandle) : ViewModel()Copy the code

Limitations of Hilt

  • Hilt does not support ContentProviders. If you want to get Hilt’s dependencies in a ContentProvider, you need to use the @entryPoint annotation. How do you use it? Do dependency injection in a class that Hilt doesn’t support

  • Limitations of Hilt in multi-module projects. Multi-module projects fall into two categories:

    • An application composed of hierarchical modules.
    • Application composed of dynamic feature modules.

An application composed of hierarchical modules

Hilt can be used for dependency injection if the multi-module project is an application consisting of hierarchical modules, as shown in the figure below. Image from Google.

In this case, you can use Hilt directly for dependency injection, just like in a single App module. I won’t go into details here. Hilt in the use of multiple modules project example HiltWithMultiModuleSimple have been uploaded to the making, with detailed comments in your code.

Dynamic function module applications

Hilt is somewhat limited if the module project is an application consisting of dynamic feature modules, or DFMs for short.

In DFMs, the interdependence between modules is reversed, so Hilt cannot be used in dynamic functional modules, so only Dagger can be used to complete dependency injection in DFMs. In DFMs, module dependency is shown in the figure below. Image from Google.

An App is divided into a Base APK and multiple module APKs.

Base APK: The APK contains basic code and resources (services, Content providers, permissions) that can be accessed by other partitioned modules. When a user requests to download your application, the APK downloads and installs it first.

Configuration APK: Each APK contains specific resources. When users download your application, their device only downloads and installs the Configuration APK for their device.

Dynamic Feature APK: Each APK contains code and resources for a Feature of the application that is not required when the application is first installed. Feature APK can be installed on demand to provide additional functionality to users. Each Feature APK depends on the Base APK.

For example dynamic-Feature1 relies on Base APK, so the way modules depend on each other in DFMs is reversed.

How to solve the Hilt component dependency problem in DFMs?

    1. In the App Module or any other module that can be handled by Hilt, define an interface and add it@EntryPointComment, and then add@InstallInAnnotations specify the scope of the Module.
// LoginModuleDependencies.kt - File in the app module.

@EntryPoint
@InstallIn(ApplicationComponent::class)
interface LoginModuleDependencies {

  @AuthInterceptorOkHttpClient
  fun okHttpClient(): OkHttpClient
}
Copy the code
    1. In the dynamic-Feature1 module, use the Dagger@ComponentAnnotate, create a Component file, and create thedependenciesPass specified in@EntryPointInterface for annotation declarations.
@Component(dependencies = [LoginModuleDependencies::class])
interface LoginComponent {

  fun inject(activity: LoginActivity)

  @Component.Builder
  interface Builder {
    fun context(@BindsInstance context: Context): Builder
    fun appDependencies(loginModuleDependencies: LoginModuleDependencies): Builder
    fun build(): LoginComponent
  }
}
Copy the code

Once the above steps are complete, you can use Dagger to do dependency injection in DFMs just as we described using Dagger earlier.

  • Initialize the Dagger in the Application and implement the HasActivityInjector interface.
  • For each ViewModel, Fragment, and Activity we need to tell Dagger how to inject them.
  • Add Fragments, Activities, and ViewModels to each module.
  • All the Activity of inheritance BaseActivity, we need to implement HasSupportFragmentInjector.

conclusion

On Hilt in multiple modules in the use of project example HiltWithMultiModuleSimple lot already upload can be to look at it.

This is the end of the Hilt starter trilogy, starting, progressing, landing, the meaning of all annotations, the project examples, the use of Jetpack components, the difference between Hilt and Dagger, and the limitations and use of multiple modules.

  • Introduction to common Hilt annotations, some of the pitfalls encountered during the implementation process, how Hilt binds to Android framework classes, and their lifecycle.
  • How to use annotations in ViewModel, App Startup, ContentProvider, etc.
  • Hilt and Dagger: How Hilt and Dagger differ, limitations and usage in multiple modules.

For those who use Dagger, you should be able to feel the feeling from starting to giving up. The cost of learning Dagger is very high. If Dagger is introduced into the project, it means that everyone in the team has to learn Dagger, which is undoubtedly a huge cost and very complicated to use. For each ViewModel, Fragment, and Activity we need to tell Dagger how to inject them.

Hilt, on the other hand, is very cheap to learn relative to Dagger, integrating the Jetpack library and Android framework classes and automatically managing their lifecycle, allowing developers to focus only on how to bind rather than managing all the Dagger configurations.

Is building a most complete and latest AndroidX Jetpack related components of the actual combat project and related components of the principle of analysis article, currently has included App Startup, Paging3, Hilt and so on, is gradually adding other Jetpack new members, the warehouse continues to update, Check it out: AndroidX-Jetpack-Practice, please give me a thumbs up if this repository is helpful to you, and I will continue to complete more project practices for new members of Jetpack.

conclusion

Committed to share a series of Android system source code, reverse analysis, algorithm, translation, Jetpack source related articles, is trying to write a better article, if this article is helpful to you to give a star, to learn together, looking forward to growing with you.

algorithm

Since LeetCode has a large question bank, hundreds of questions can be selected for each category. Due to the limited energy of each person, it is impossible to brush all the questions. Therefore, I sorted the questions according to the classic types and the difficulty of the questions.

  • Data structures: arrays, stacks, queues, strings, linked lists, trees…
  • Algorithms: Search algorithm, search algorithm, bit operation, sorting, mathematics,…

Each problem will be implemented in Java and Kotlin, and each problem has its own solution ideas, time complexity and space complexity. If you like algorithms and LeetCode like me, you can pay attention to my LeetCode problem solution on GitHub: Leetcode-Solutions-with-Java-And-Kotlin, come to learn together And look forward to growing with you.

Android 10 source code series

I’m writing a series of Android 10 source code analysis articles. Knowing the system source code is not only helpful in analyzing problems, but also very helpful in the interview process. If you like to study Android source code as MUCH as I do, You can follow my Android10-source-Analysis on GitHub, and all articles will be synchronized to this repository.

  • How is APK generated
  • APK installation process
  • 0xA03 Android 10 source code analysis: APK loading process of resource loading
  • Android 10 source code: APK
  • Dialog loading and drawing process and use in Kotlin, DataBinding
  • WindowManager View binding and architecture
  • 0xA07 Android 10 source code analysis: Window type and 3d view hierarchy analysis
  • More……

Android Apps

  • How to package Kotlin + Android Databinding in your project
  • Bye-bye buildSrc, embrace Composing builds for faster Android builds
  • Few people know Kotlin’s technique and principle analysis
  • AndroidX App Startup practice and principle analysis of Jetpack’s latest member
  • Jetpack member Paging3 Practice and Source Code Analysis (PART 1)
  • Jetpack New Member Paging3 Network Practice and Principle Analysis (II)
  • Jetpack’s new member Hilt practices (1) Starting a pit
  • Jetpack new member Hilt’s App Startup practice (2) Advanced chapter

Select a translation

At present, I am sorting out and translating a series of selected foreign technical articles. Besides translation, many excellent English technical articles provide good ideas and methods. Every article has a part of the translator’s thinking and a deeper interpretation of the original text. You can pay attention to my Technical-Article-Translation on GitHub, and articles will be synchronized to this warehouse.

  • [Google engineers] just released a new Fragment feature, “New ways to transfer Data between Fragments” and source code analysis
  • How does FragmentFactory elegantly use Koin and partial source code analysis
  • [2.4K Start] Drop Dagger to Koin
  • [5K +] Kotlin’s performance optimization stuff
  • Decrypt RxJava’s exception handling mechanism
  • [1.4K+ Star] Picasso
  • More……

Tool series

  • Shortcuts to AndroidStudio that few people know
  • Shortcuts to AndroidStudio that few people know
  • All you need to know about ADB commands
  • 10 minutes introduction to Shell scripting
  • Dynamically debug APP based on Smali file Android Studio
  • The Android Device Monitor tool cannot be found in Android Studio 3.2