Dagger in Multi-Module Clean Applications
In this tutorial, you’ll learn how to integrate Dagger in an Android multi-module project built using the clean architecture paradigm. By Pablo L. Sordo Martinez.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Dagger in Multi-Module Clean Applications
30 mins
- Getting Started
- Covering Key Concepts
- Multi-Module App
- Clean Architecture
- Dependency Injection With Dagger
- Analyzing the Problem
- Building Up the Dagger Graph
- Connecting the Data Layer
- Populing the Data Layer
- Domain Layer
- Presentation Layer
- Getting Familiar with Components and Subcomponents
- Buliding Activity Components
- Building the Application Component
- Wrapping Everything Up
- Performance Assessment
- Testing
- Testing the Presenter
- Testing the Use Case
- Testing the Repository
- Where to Go From Here?
Getting Familiar with Components and Subcomponents
Dagger is mainly built upon modules and components, but it recently added a third entity: subcomponents. According to the documentation, Subcomponents are components that inherit and extend the object graph of a parent component.
In this application, you’ll create a component called ApplicationComponent
and two subcomponents called SplashComponent
and MainComponent
.
The idea behind this approach is:
-
ApplicationComponent
will include all dependencies declared in both data-layer and domain-layer. It’s the container of entities ready to be injected when needed. It’ll also include the subcomponents, but in a particular way you’ll see later. -
SplashComponent
andMainComponent
will comprise the dependencies corresponding to the respective feature in the presentation-layer. - You’ll implement a mechanism to build the dependency graph through
ApplicationComponent
. - Moreover, you’ll define an approach to inject any of the subcomponents. This will trigger any dependency insertion in your code, since the subcomponents will have access to all the entities in
ApplicationComponent
.
Buliding Activity Components
Navigate again to PresentationlayerModule.kt
and add the following snippet:
@ActivityScope
@Subcomponent(modules = [SplashModule::class]) // 1
interface SplashComponent {
// 2
@Subcomponent.Factory
interface Factory {
fun create(module: SplashModule): SplashComponent
}
// 3
fun inject(activity: SplashActivity)
}
// 4
interface SplashComponentFactoryProvider {
fun provideSplashComponentFactory(): SplashComponent.Factory
}
It’s especially remarkable that:
- A subcomponent definition uses the
@Subcomponent
annotation. It can be comprised of any Dagger module you want. In this case, it’sSplashModule
. - Since the included module has a non-empty constructor, you need to declare a subcomponent factory to build it.
- The subcomponent will be injected into
SplashActivity
only. -
SplashComponentFactoryProvider
is the mechanism you’ll use with this subcomponent — more on this later.
Similar to what you did with the previous subcomponent, add the following snippet to PresentationlayerModule.kt
:
@ActivityScope
@Subcomponent(modules = [MainModule::class])
interface MainComponent {
@Subcomponent.Factory
interface Factory {
fun create(module: MainModule): MainComponent
}
fun inject(activity: MainActivity)
}
interface MainComponentFactoryProvider {
fun provideMainComponentFactory(): MainComponent.Factory
}
The approach is similar, and the two subcomponents are ready to be used. However, as stated before, you need to include them inside ApplicationComponent
. You can achieve this by including them inside a new Dagger module. Take the same file and add this snippet:
@Module(subcomponents = [SplashComponent::class, MainComponent::class])
object PresentationlayerModule
PresentationlayerModule
is a module consisting of two subcomponents, which come with their respective dependencies.
Building the Application Component
Time to implement the component that will build the application graph. Navigate to app and create a folder called “di” and a file called ApplicationGraph.kt
. Then add the following:
@ApplicationScope // 1
// 2
@Component(
modules = [UtilsModule::class, PresentationlayerModule::class, UsecaseModule::class, RepositoryModule::class, DatasourceModule::class])
interface ApplicationComponent {
// 3
@Component.Factory
interface Factory {
fun create(modules: UtilsModule): ApplicationComponent
}
// 4
fun splashComponentFactory(): SplashComponent.Factory
fun mainComponentFactory(): MainComponent.Factory
}
// 5
@Module
class UtilsModule(private val ctx: Context) {
@ApplicationScope
@Provides
fun provideApplicationContext(): Context = ctx
}
It’s important to note that:
- The scope of this component is set to
@ApplicationScope
, which is wider than any other scope in the app. - The list of modules composing this component includes all the implementations seen thus far and a new module called
UtilsModule
, which is defined at the end of the file. - Since
UtilsModule
will have a non-empty constructor, you need to include a component factory. - If the component includes any subcomponent, as in this case, you’ll need to expose it if you want to use it later.
-
UtilsModule
is a standard way to expose the application context as a dependency in Dagger.
Wrapping Everything Up
After defining all the required Dagger entities, now you’ll build the application graph. Navigate to app
, open BaseApplication.kt
and replace the class definition with the following:
class BaseApplication : Application(), SplashComponentFactoryProvider, MainComponentFactoryProvider { // 1
private lateinit var appComponent: ApplicationComponent
override fun onCreate() {
super.onCreate()
// 2
appComponent = DaggerApplicationComponent.factory().create(modules = UtilsModule(ctx = this))
}
// 3
override fun provideSplashComponentFactory(): SplashComponent.Factory =
appComponent.splashComponentFactory()
override fun provideMainComponentFactory(): MainComponent.Factory =
appComponent.mainComponentFactory()
}
The above implementation needs a few comments:
- In addition to extending
Application
,BaseApplication
implements the two factory provider interfaces defined for each of the subcomponents. This means you’ll useBaseApplication
to build subcomponents and inject dependencies. - The
appComponent
variable holds a reference toDaggerApplicationComponent
. Dagger is responsible for creating this instance when the project compiles. Use the component factory to indicate any external dependency, such as the application context. - You can override the component factory functions using the dependencies exposed when defining
ApplicationComponent
.
Once you’ve indicated how to build the application graph, trigger the dependency chain in the application entry points. In Android, this is normally done in the activities.
Open SplashActivity
and delete the presenter
variable (line 34). Then, copy the following snippet into the class definition:
// 1
@Inject
@Named(SPLASH_PRESENTER_TAG)
lateinit var presenter: SplashContract.Presenter
override fun onCreate(savedInstanceState: Bundle?) {
getSplashComponent().inject(this) // 2
super.onCreate(savedInstanceState)
}
In the code above:
- This annotation tells Dagger that this class needs an instance of a
SplashContract.Presenter
withSPLASH_PRESENTER_TAG
as the qualified name. - You need to tell Dagger where to take the above dependency from. You can do this by including an extension function in the same file and invoking it from
onCreate
.
private fun SplashActivity.getSplashComponent(): SplashComponent =
(application as SplashComponentFactoryProvider).provideSplashComponentFactory().create(module = SplashModule(this))
Now, repeat the process with MainActivity
. Open the class, remove the presenter
variable and replace the onCreate
function with:
@Inject
@Named(MAIN_PRESENTER_TAG)
lateinit var presenter: MainContract.Presenter
override fun onCreate(savedInstanceState: Bundle?) {
getMainComponent().inject(this)
super.onCreate(savedInstanceState)
viewBinding = ActivityMainBinding.inflate(layoutInflater)
initView()
setContentView(viewBinding.root)
}
Once again, you’d better use an extension function. Add the following implementation at the end of the file for getMainComponent()
:
private fun MainActivity.getMainComponent(): MainComponent =
(application as MainComponentFactoryProvider).provideMainComponentFactory()
.create(module = MainModule(this))
And that’s all. You’ve finished implementing Numberizer. Congratulations!
DaggerApplicationComponent
.Performance Assessment
Once completed, your app should be functional, like in the following demo video:
When started, the splash screen appears for a few seconds, and then it jumps to main view. At this screen, the user can select a category from a drop-down menu and type a number. When the button gets tapped, and after some loading, a message displays showing a fact related to the number introduced.