Reverse Dagger 2 Component Subcomponent Dependency

When we use Dagger 2, it is very common to have Component and Subcomponent.

If you want to learn about Dagger 2, or it’s subcomponent, click the linked on the word itself.

Normally their relationship is as below where the Subcomponent access it’s Parent Component data as dependences.

However, is it possible to do the reverse as diagram below, where the Parent Component access it’s Subcomponent data as dependence?

The normal approach (Subcomponent depends on Component)

The code below showing you, where you inject the Person from the MySubComponent. The Person in the MySubcomponent depends on the String from MyComponent.

class MainActivity : AppCompatActivity() {

@Inject lateinit var person: Person

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val component = DaggerMyComponent.create()
val subComponent = component.getSubComponentBuilder()
.mySubModule(MySubModule()).build()
subComponent.inject(this)

Log.d("Trace", person.name)
}
}

@Component(modules = [MyModule::class])
interface MyComponent {
fun getSubComponentBuilder(): MySubComponent.Builder
}

@Module
class MyModule {
@Provides
fun providesName() = "Jake W"
}

@Subcomponent(modules = [MySubModule::class])
interface MySubComponent {
fun inject(mainClass: MainActivity)

@Subcomponent.Builder
interface Builder {
fun mySubModule(module: MySubModule): Builder
fun build(): MySubComponent
}
}

@Module
class MySubModule {
@Provides
fun providePerson(name: String) = Person(name)
}

class Person(val name: String)

This is the conventional approach that could be seen in most tutorial.

The reverse approach (Component depends on Subcomponent)

However, we’re thinking how nice our Component could access an encapsulated Subcomponent, that we reverse the relationship.

To demonstrate that, I use the same code, but swap the Person and String object creation, where Person is now in MyComponent while String is now in MySubComponent.

With that, we would need the relationship as below. How could we do that now?

The main gist of it is to @Module(Subcomponent = [SubcomponentDependOn::class] as shown int he code below.

@Module(subcomponents = [MySubComponent::class])
class MyModule {
   @Provides
fun providesInfo(subComponentBuilder: MySubComponent.Builder)
: Person {
val subComponent = subComponentBuilder
.mySubModule(MySubModule()).build()
return Person(subComponent.getName())
}
}

Then in MyModule you could create your Person data accessing the Subcomponent dependencies accordingly as shown above.

To make it clearer, the entire code as below. Do see the inject is done at the component level.

class MainActivity : AppCompatActivity() {
    @Inject
lateinit var person: Person
    override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val component = DaggerMyComponent.create()
component.inject(this)
Log.d("Trace", person.name)
}
}
@Component(modules = [MyModule::class])
interface MyComponent {
fun inject(mainClass: MainActivity)
}
@Module(subcomponents = [MySubComponent::class])
class MyModule {
    @Provides
fun providesInfo(subComponentBuilder: MySubComponent.Builder)
: Person {
val subComponent = subComponentBuilder
.mySubModule(MySubModule()).build()

return Person(subComponent.getName())
}
}
@Subcomponent(modules = [MySubModule::class])
interface MySubComponent {
fun getName() : String
    @Subcomponent.Builder
interface Builder {
fun mySubModule(module: MySubModule): Builder
fun build(): MySubComponent
}
}
@Module
class MySubModule {
@Provides fun providesName() = "Jake W"
}
class Person(val name: String)

I hope this post is helpful to you. You could check out my other interesting topics here.

Follow me on medium, Twitter or Facebook for little tips and learning on Android, Kotlin etc related topics. ~Elye~