Search code examples
androiddagger-2dagger

Unable to access Activity Component from Fragment Component in Dagger(Android)


I am trying to create dependent components for Activity and Fragment. Here's my Activity Component:

@Component(modules = [ActivityModule::class],
        dependencies = [AppComponent::class])

interface ActivityComponent{
    fun inject(activity: Activity)

    @Component.Builder
    interface Builder{
        fun appComponent(component: AppComponent): Builder
        fun activityModule(activityModule: ActivityModule): Builder
        fun build(): ActivityComponent
    }
}

@Module
class ActivityModule(private val activity: Activity) {
    @Provides
    fun provideActivity() = activity
}

Fragment Component:

@Component(modules = [FragmentModule::class],
        dependencies = [ActivityComponent::class])

interface FragmentComponent{
    fun inject(fragment: Fragment)

    @Component.Builder
    interface Builder{
        fun activityComponent(activityComponent: ActivityComponent): Builder
        fun fragmentModule(fragmentModule: fragmentModule): Builder
        fun build(): FragmentComponent
    }
}

@Module
class FragmentModule(private val fragment: Fragment) {
    @Provides
    fun provideFragment() = fragment
}

I am getting a compile error in places where I am trying to access activity.

Activity cannot be provided without an @Provides-annotated method.

It's my understanding that since Activity has already been injected in the ActivityComponent, and Fragment is simply adding itself to the ActivityComponent, it should get all the dependencies provided by ActivityComponent and AppComponent's modules. I see this happening in ActivityComponent, where I get dependencies from AppComponent automatically.

Update: If I add the following code to the FragmentModule, I am able to get the code compiled:

@Provides
fun provideActivity() = fragment.context as Activity

But this is not making sense to me, as I was hoping to get the pre-injected Activity from ActivityComponent


Solution

  • I realized that parent components should always declare the dependencies they want to expose. Otherwise dependent components cannot access them just by declaring them as dependents. Note that transitive dependencies don't work either. So AppComponent has to declare a dependency for ActivityComponent to use it - but ActivityComponent has to re-declare the same dependency if FragmentComponent needs it.

    interface ActivityComponent{
        fun inject(activity: Activity)
    
        @Component.Builder
        interface Builder{
            fun appComponent(component: AppComponent): Builder
            fun activityModule(activityModule: ActivityModule): Builder
            fun build(): ActivityComponent
        }
        var activity: Activity
    }