Elye
Elye
Sep 17 · 4 min read

In my previous article, I discussed setting up Dagger 2 for a modular app. This article will assume we are building a whole new app. The problem for many of us is that our apps have been around for ages — and maybe they were built based on single module. The question becomes: Is it still possible to slowly modularize it using Dagger 2 for injection?


Problem Statement

To illustrate the problem, below is a diagram. Imagine we used to have an AppModule that contained everything. To start modularizing our work, we want to split out the FeatureOneActivity into another module, e.g., FeatureOneModule.

However, we have one problem: How can we access the AppNetwork and AppRepository? We don’t want to create a new one.

One way is to move everything down to the BaseModule that is linked by all, as per my previous article. However, at times this is not easy, especially if there’s a lot of code involved in the change.


Solution

So how can we solve this without needing to move things all at once? This is done in three steps.

1. Create a BaseModule with all needed dependencies

First of all, identify the dependencies needed by using FeatureOneActivity. If it can be moved to the BaseModule, that’s great. If not, create an interface for them at the BaseModule with all the needed API exposed.

In our case below, we need to:

  • Create the BaseModule
  • Create the BaseNetwork and BaseRepository interfaces in the BaseModule
  • Let AppNetwork and AppRepository implement the respective interfaces
  • In the MainComponent, create the BaseNetwork and BaseRespository instead

2. Create BaseSubComponent within a BaseApplication

Now, let’s get Dagger 2 set up in the BaseModule so FeatureOneModule can access it.

This is done as per the diagram below. More explanation beneath.

The above diagram shows how it was done, as explained below.

  • Introduce a Dagger 2 SubComponent that has access to BaseNetwork and BaseRepository
@Subcomponent
interface BaseSubComponent {
val baseNetwork: BaseNetwork
val baseRepository: BaseRepository
}
  • Let the MainComponent contain and create the BaseSubComponent
@Singleton
@Component(modules = [AppNetworkModule::class, AppRepositoryModule::class])
interface MainComponent {
fun inject(activity: MainActivity)
// MainComponent contains BaseSubComponent
val baseSubComponent: BaseSubComponent

}
  • Create a BaseApplication that one could access in the BaseSubComponent
abstract class  BaseApplication: Application() {
abstract fun getBaseSubComponent(): BaseSubComponent
}
  • In the MainApplication, you’ll fine it now extends from BaseApplication and that there’s now an override function that provides the BaseSubComponent
class MainApplication: BaseApplication() {
companion object {
val baseComponent by lazy {
DaggerMainComponent.create()
}
}

override fun getBaseSubComponent(): BaseSubComponent {
return baseComponent.baseSubComponent
}

}

3. Create a FeatureComponent that depends on the SubComponent

Lastly, we must link up the FeatureComponent and get the dependencies injected there.

This is done by:

  • Create a new Dagger 2 component within the FeatureOneModule. The component (e.g., FeatureOneComponent) will be dependent on the SubComponent. This allows the FeatureOneComponent to have access to any SubComponent-provided object (e.g. the BaseNetwork and BaseRepository).
@Component(dependencies = [BaseSubComponent::class], modules = [FeatureOneDependentModule::class])
interface FeatureOneComponent {
fun inject(featureOneActivity: FeatureOneActivity)
}
  • In the FeatureOneActivity, create the new Dagger 2 FeatureOneComponent graph, access the SubComponent using the BaseApplication, and inject the dependency into FeatureOneActivity
class FeatureOneActivity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_feature_one)

DaggerFeatureOneComponent.builder()
.baseSubComponent(
(application as BaseApplication)
.getBaseSubComponent())
.build()
.inject(this)

}
}

That’s it. Now FeatureOneActivity can access the shared BaseNetwork and BaseRepository, even though they are created at the AppModule (e.g., the module above FeatureOneModule).

The above diagram looks more complicated than it is due to the additional details of the dependent objects (e.g., network and repository) shown. If we remove them from the diagram, this would simplify the diagram into simpler Dagger 2 component relationships across modules.

From the diagram below, one can clearly see that in order for FeatureOneModule to access AppModule-created dependencies, you just need to have its component graph (FeatureComponent) dependent on a SubComponent created by MainComponents.

With this approach, you can slowly create your new features outside the AppModule or slowly migrate your features from your AppModule — without needing to worry about whether the dependencies are set up on the AppModule.

To get a clearer picture, you can get the code below:

Better Programming

Advice for programmers.

Elye

Written by

Elye

Learning and Sharing Android and iOS Development

Better Programming

Advice for programmers.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade