Jorge E. Hernández
Jorge E. Hernández

Reputation: 2938

How to @Inject members in BaseActivity using dagger.android?

This is my scenario:

This is the rest of my components and modules:

This is the compilation error message:

Error: [dagger.android.AndroidInjector.inject(T)]
android.support.v4.app.FragmentManager cannot be provided
without an @Provides- or @Produces-annotated method.

Error:A binding with matching key exists in component:
com.myapp.ActivityBindingModule_BindBaseActivity.BaseActivitySubcomponent

Upvotes: 1

Views: 4687

Answers (2)

Roger Alien
Roger Alien

Reputation: 3060

Main idea to understand that Dagger understands only top component/activity injection and doesn't see BaseActivityComponent

Alternative implementation to @David Medenjak solution is to simply include BaseActivityModule and inject BaseActivity:

@ActivityScope
@Subcomponent(modules = {MainActivityModule.class, BaseActivityModule.class} )
public interface PlayerActivityComponent {

    void inject(MainActivity activity);

    void inject(BaseActivity activity);
}

And you can inject your objects now both in BaseActivity and MainActivity

Upvotes: 1

David Medenjak
David Medenjak

Reputation: 34522

The problem lies with your assumption that you need to inject a BaseActivity at some point...

@Module
abstract class ActivityBindingModule {
  @ContributesAndroidInjector
  abstract fun bindMainActivity(): MainActivity


  // ...that's not really how it works... :/
  @ContributesAndroidInjector(modules = [(BaseActivityModule::class)])
  abstract fun bindBaseActivity(): BaseActivity
}

Using the above code you end up with some component to inject MainActivity, and some component to inject BaseActivity, but neither can inject "both". The error you receive is because MainActivity can't supply the FragmentManager it needs to inject in its parent BaseActivity. It's missing the module to do so. You only add the BaseActivityModule to your other component, to which the MainActivityComponent effectively has no access—hence the cannot be provided error.

Dagger always needs to inject the whole object. There is no partial injection, or injecting from multiple components at once. If a single component can't provide all the dependencies it needs you will get an error. Your fun bindBaseActivity(): BaseActivity is useless, because you will never use BaseActivity, but you will only use MainActivity or other subclasses of it. Those components need to be able to provide the dependencies of the BaseActivity as well.


If you want to inject dependencies in the BaseActivity you need to add a module that provides the necessary bindings. Your code should end up looking like the following:

@Module
abstract class ActivityBindingModule {
  @ContributesAndroidInjector(modules = [BaseActivityModule::class, MainActivityModule::class])
  abstract fun bindMainActivity(): MainActivity

  // no BaseActivity component necessary
}

@Module
abstract class MainActivityModule {
  @Binds
  abstract fun bindBaseActivity(activity: MainActivity) : BaseActivity
}

This does the following things:

  1. It adds BaseActivityModule to the MainActivityComponent, so that your base-dependencies can be provided by this component and MainActivity can be injected
  2. It binds MainActivity in another module as your BaseActivity so that you can use that in your module and don't have to bind a FragmentManager for every activity you have

While you can reuse BaseActivityModule and add it to all of your activity implementations, you will have to add a module to bind the activity as a BaseActivity for every one of your activities.

There might be a more optimized approach, but that's the vanilla requirements for injecting subclasses.

Upvotes: 7

Related Questions