Dagger 2.16多Activity注入报错:匹配绑定存在却提示缺失
我用Dagger 2.16实现依赖注入,参考了一篇Medium文章,只有HomeActivity时一切正常,但添加SplashScreenActivity并接入Dagger后出现了构建报错。
项目相关代码
AppComponent.kt
@Singleton @Component(modules = [ AndroidInjectionModule::class, AppModule::class, ActivityBuilder::class, ServiceBuilder::class, BroadcastRecieverBuilder::class]) interface AppComponent : AndroidInjector<MyApp> { @Component.Builder abstract class Builder : AndroidInjector.Builder<MyApp>() }
AppModule.kt
@Module() class AppModule { @Provides @Singleton fun provideContext(application: MyApp): Context { return application } @Provides @Singleton fun provideRestService(retrofit: Retrofit): RestService { return retrofit.create(RestService::class.java) } ... }
ActivityBuilder.kt
@Module abstract class ActivityBuilder { @ContributesAndroidInjector(modules = [HomeActivityModule::class]) @PerActivity abstract fun bindHomeActivity(): HomeActivity @ContributesAndroidInjector(modules = [SplashScreenModule::class]) @PerActivity abstract fun bindSplashActivity(): SplashScreenActivity }
BaseActivity.kt
abstract class BaseActivity<V : BaseView, P : MvpBasePresenter<V>> : MvpActivity<V, P>(), BaseView, HasSupportFragmentInjector { @Inject lateinit var fragmentInjector: DispatchingAndroidInjector<Fragment> @Inject lateinit var mPresenter: P override fun onCreate(savedInstanceState: Bundle?) { AndroidInjection.inject(this) super.onCreate(savedInstanceState) } override fun createPresenter(): P = mPresenter override fun supportFragmentInjector(): AndroidInjector<Fragment> { return fragmentInjector } }
注:我没有使用DaggerActivity,而是自定义BaseActivity继承自Mosby的MvpActivity。
SplashScreenModule.kt
@Module abstract class SplashScreenModule { @Binds @PerActivity internal abstract fun splashPresenter(splashPresenter: SplashScreenPresenter): BasePresenter<*> }
HomeActivityModule.kt
@Module abstract class HomeActivityModule { @Binds @PerActivity internal abstract fun homePresenter(homePresenter: HomeActivityPresenter): BasePresenter<*> @ContributesAndroidInjector(modules = [DownloadFragmentModule::class]) @PerFragment internal abstract fun downloadsFragment(): DownloadsFragment }
构建报错信息
error: [Dagger/MissingBinding] [dagger.android.AndroidInjector.inject(T)] java.util.Map<java.lang.Class<? extends android.support.v4.app.Fragment>,javax.inject.Provider<dagger.android.AndroidInjector.Factory<? extends android.support.v4.app.Fragment>>> cannot be provided without an @Provides-annotated method. public abstract interface AppComponent extends dagger.android.AndroidInjector<com.realtime.app.MyApp> { ^ A binding with matching key exists in component: com.realtime.dagger.ActivityBuilder_BindHomeActivity.HomeActivitySubcomponent java.util.Map<java.lang.Class<? extends android.support.v4.app.Fragment>,javax.inject.Provider<dagger.android.AndroidInjector.Factory<? extends android.support.v4.app.Fragment>>> is injected at dagger.android.DispatchingAndroidInjector.<init>(injectorFactories) dagger.android.DispatchingAndroidInjector<android.support.v4.app.Fragment> is injected at com.realtime.core.BaseActivity.fragmentInjector com.realtime.splashScreen.SplashScreenActivity is injected at dagger.android.AndroidInjector.inject(T) component path: com.realtime.dagger.AppComponent → com.realtime.dagger.ActivityBuilder_BindSplashActivity.SplashScreenActivitySubcomponent
我试过查看类似问题,但没法对应到自己的场景,请问我遗漏了什么?
更新:目前我让SplashScreenActivity不继承BaseActivity,避免注入fragmentInjector: DispatchingAndroidInjector<Fragment>,暂时可以正常运行,因为SplashScreenActivity中没有Fragment。
其实问题出在你的BaseActivity实现了HasSupportFragmentInjector接口,并且注入了DispatchingAndroidInjector<Fragment>,但SplashScreenActivity的子组件里没有任何Fragment的注入绑定。
Dagger在生成SplashScreenActivitySubcomponent的时候,发现需要提供Map<Class<? extends Fragment>, Provider<AndroidInjector.Factory<? extends Fragment>>>这个依赖,但SplashScreenModule里没有相关的Fragment贡献代码,也没有引入AndroidSupportInjectionModule(注意不是AndroidInjectionModule)到它的子组件中,所以Dagger找不到对应的绑定。
具体解决方案有两种,你可以根据需求选择:
方案1:给SplashScreen的子组件添加必要的模块
修改ActivityBuilder.kt中SplashScreen的@ContributesAndroidInjector,加入AndroidSupportInjectionModule::class:
@ContributesAndroidInjector(modules = [SplashScreenModule::class, AndroidSupportInjectionModule::class]) @PerActivity abstract fun bindSplashActivity(): SplashScreenActivity
这个模块会提供Dagger需要的Fragment注入相关的基础绑定,即使你的SplashScreenActivity没有Fragment,也能满足DispatchingAndroidInjector的依赖需求。
方案2:拆分BaseActivity,区分带Fragment和不带Fragment的场景
既然SplashScreenActivity不需要处理Fragment,你可以创建两个BaseActivity:
- 一个是带Fragment注入支持的
BaseFragmentActivity(继承MvpActivity,实现HasSupportFragmentInjector,注入fragmentInjector),供HomeActivity这类有Fragment的页面继承; - 另一个是不带Fragment注入的
BaseMvpActivity(只处理Presenter注入,不实现HasSupportFragmentInjector,也不注入fragmentInjector),供SplashScreenActivity这类无Fragment的页面继承。
这样更符合单一职责原则,也能避免不必要的依赖注入。
为什么HomeActivity没问题?
因为HomeActivityModule里有@ContributesAndroidInjector用于DownloadsFragment,Dagger会自动在HomeActivity的子组件中引入必要的Fragment绑定依赖,所以不会报错。而SplashScreen的子组件里没有任何Fragment相关的绑定,也没加AndroidSupportInjectionModule,就会出现MissingBinding的错误。
内容的提问来源于stack exchange,提问作者Rishabh876

