问题
This is my scenario:
- I want all my Activities to inherit from
BaseActivity
Within the
BaseActivity
, I want to injectNavigator
(it helps me to manage theFragments
backstack and navigate between activities):abstract class BaseActivity : DaggerAppCompatActivity() { @Inject lateinit var navigator: Navigator @Inject lateinit var prefs: SharedPreferences // injected via AppModule.kt, see below. }
The
Navigator
class needs aFragmentManager
in its constructor:class Navigator @Inject constructor( val fragmentManager: FragmentManager) { // class body }
I want to provide
FragmentManager
from aBaseActivity
object inBaseActivityModule
:@Module class BaseActivityModule { @PerActivity @Provides fun provideFragmentManager(baseActivity: BaseActivity): FragmentManager { return baseActivity.supportFragmentManager } }
This is the rest of my components and modules:
AppComponent.kt
@Singleton @Component(modules = [ AndroidSupportInjectionModule::class, AppModule::class, ActivityBindingModule::class]) interface AppComponent { @Component.Builder interface Builder { @BindsInstance fun application(application: Application): Builder fun build(): AppComponent } fun inject(app: AndroidApplication) }
AppModule.kt
@Module class AppModule { @Singleton @Provides fun providesPrefs(application: Application): SharedPreferences { return application.getSharedPreferences("MyPrefs", Context.MODE_PRIVATE) } }
ActivityBindingModule.kt
@Module abstract class ActivityBindingModule { @ContributesAndroidInjector abstract fun bindMainActivity(): MainActivity @ContributesAndroidInjector(modules = [(BaseActivityModule::class)]) abstract fun bindBaseActivity(): BaseActivity }
BaseActivityModule.kt
@Module class BaseActivityModule { @Provides fun provideFragmentManager(baseActivity: BaseActivity): FragmentManager { return baseActivity.supportFragmentManager } }
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
回答1:
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:
- It adds
BaseActivityModule
to the MainActivityComponent, so that your base-dependencies can be provided by this component andMainActivity
can be injected - It binds
MainActivity
in another module as yourBaseActivity
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.
回答2:
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
来源:https://stackoverflow.com/questions/48533899/how-to-inject-members-in-baseactivity-using-dagger-android