Dagger
Dagger is the most popular dependency injection, especially in Android development. Previously Dagger 2 was the option when building an App, but now there is a new dependency injection called Dagger Hilt that has a lot of improvement from it predecessor (Dagger 2).
Multi Module App
Nowadays in the Android development world, big companies are likely to make their app a multi module app, because it has a lot of benefits like the build speed, dynamic delivery module, ect. so Dagger (espesially Hilt) must be able to manage a dependency for this kind of features
But what kind of improvement does hilt has? How can we use Hilt in a multi module or dynamic feature module (DFM) app?
So here we will learn about improvement that Hilt has over Dagger 2, also how to implement Hilt in DFM App 🙌
⚠️ In this article we are not showing how to add the library, and the step2 for creating dagger dependency, you can learn it by looking at this Repository movieapp as it will be the example of the app we use in this article
Hilt improvement from Dagger 2
As we know Hilt is the successor of the Dagger 2, that’s why Hilt have a lot of improvement, like examples below
1. No Component Interface in Hilt
In dagger hilt we don’t have to create the Component Interface as our dagger entry point, We just have to add @AndroidEntryPoint
Dagger 2
@AppScope@Component(dependencies = [CoreComponent::class],modules = [AppModule::class, ViewModelModule::class])interface AppComponent {@Component.Factoryinterface Factory {fun create(coreComponent: CoreComponent): AppComponent}fun inject(activity: MainActivity)fun inject(fragment: HomeFragment)}----------------------------------------------------------------(requireActivity().applicationContext as App).appComponent.inject(this)
Hilt
@HiltAndroidAppopen class App: Application()@AndroidEntryPointclass HomeFragment: Fragment()@AndroidEntryPointclass MainActivity : AppCompatActivity()
For Dagger 2 We have to create AppComponent as it will be the entry point for dagger, and inject the dependency in Activity and Fragment class
In Hilt we just have to add @HiltAndroidApp for Application class and @AndroidEntryPoint for Activity and Fragment class. then we already make the component entry point for dagger
2. Hilt provide application context
For Dagger 2 we still need to inject the context by injecting it in the view, but for hilt we just have to use the annotation of @ApplicationContext to provide the application context
Dagger 2
@Singleton@Providesfun provideDatabase(context: Context): TMDBDataBase = Room.databaseBuilder(context,TMDBDataBase::class.java, "TMDBDataBase.db")--------------------------------------------------------------------------------(requireActivity().applicationContext as App).appComponent.inject(applicationContext)
Hilt
@Singleton@Providesfun provideDatabase(@ApplicationContext context: Context): TMDBDataBase = Room.databaseBuilder(context,TMDBDataBase::class.java, "TMDBDataBase.db")
3. Hilt has it standardize scope
Hilt has standardize the component scope without us need to write it
(more info: hilt components)
Dagger 2
@Scope@Retention(AnnotationRetention.RUNTIME)annotation class AppScope--------------------------------------------------------------------------------@AppScope@Component(dependencies = [CoreComponent::class],modules = [AppModule::class, ViewModelModule::class])interface AppComponent {@Component.Factoryinterface Factory {fun create(coreComponent: CoreComponent): AppComponent}fun inject(activity: MainActivity)fun inject(fragment: HomeFragment)}
Hilt
@Module@InstallIn(SingletonComponent::class)abstract class AppModule {@Bindsabstract fun provideMovieUseCase(movieInteractor: MovieInteractor): MovieUseCase}
In Dagger 2 we need to create our own scope (example: @AppScope) to set the lifecycle of our component
In Hilt there are already standardized components that work on its own lifecycle like SingletonComponent, ViewModelComponent. ActivityComponent, FragmentComponent, ect.
Yes that correct, for MVVM architecture, Hilt support ViewModel Scope so we don’t have to bind the ViewModel like when we use the dagger 2
Hilt In Dynamic Feature App
So for all knowledge we have above, it just works when our app is a single module app or multi module app with a standard module. But In the DFM app, Hilt can’t obtain the dependency, because Hilt uses a monolithic component system which is incompatible with DFM that loads classes dynamically. so for a DFM app we have to use the old dagger way with the help of @EntryPoint annotation. Yes after all of this you should learn dagger 2 as well 😁
but we can still use the benefit of hilt component scope, so we don’t have to create the scope our self 🤩
Dynamic Feature App Structure
So for better understanding we will use this structure of application where there are one base module that is :app, two dynamic feature module of :moviedetail and :favmovie, and one module that will be use by three of them called :core.
The app it self can be found here movieapp
The core module is the place for providing the database and remote data, then the module will call the MovieUseCase for getting the access to them, but the module that can access the MovieUseCase is just only the :app module, because hilt can’t obtain it dependency to the dynamic feature app (:moviedetail and :favmovie)
The dependency of DFM is reversed, so the :app module cannot access any of DFM classes and the dagger tree cannot be constructed. but dont worry, hilt give us the solution.
To provide the dynamic feature app, we can obtain it by using the @EntryPoint annotation
@EntryPoint@InstallIn(SingletonComponent::class)interface SubModuleDependencies {fun proiveMovieUseCase(): MovieUseCase}
everything that we provide inside SubModuleDependencies interface will be obtained outside the scope of hilt, so the DFM can get the dependency of the MovieUseCase. Remember we have to write the SubModuleDependencies interface on the hilt scope.
And then we can create the component that need the SubmoduleDependencies as it dependencies
@Component(dependencies = [SubModuleDependencies::class],modules = [FavMovieViewModelModule::class])@Singletoninterface FavMovieComponent {fun inject(activity: FavMovieActivity)@Component.Builderinterface Builder {fun context(@BindsInstance context: Context): Builderfun dependencies(component: SubModuleDependencies): Builderfun build(): FavMovieComponent}}
Then we can provide the SubModuleDependencies using the EntryPointAccessors, after that we can inject the FavMovieComponent in the DFM Activity or Fragment
DaggerFavMovieComponent.builder().context(applicationContext).dependencies(EntryPointAccessors.fromApplication(applicationContext,SubModuleDependencies::class.java)).build().inject(this)
And then build the project, see the project will run perfectly without error
Congratulations we have implemented Hilt in our DFM project! 🥳
For more detail about the implementation, you can see this DFM App movieapp, then checkout branch
- master for Dagger 2
- hilt_multi_module for Hilt
Thank you for visiting this article, hope it can be useful for us 😁
For more info you can check this link below
- https://developer.android.com/training/dependency-injection/dagger-multi-module
- https://medium.com/androiddevelopers/hilt-and-dagger-annotations-cheat-sheet-9adea070e495
- https://dagger.dev/hilt/benefits.html
- https://dagger.dev/hilt/components.html
- https://medium.com/androiddevelopers/dagger-code-generation-cheat-sheets-6b4fa2da4e7a