Dagger2:如何复制模块组件forActivity / Fragments,依靠单个Base(模块/组件)for Base(Activity / Fragment)&

时间:2018-03-07 13:23:27

标签: android dependency-injection dagger-2 dagger

给定BaseFragment及其子类:DerivedFragmentA,DerivedFragmentB,......

让我们说,大多数@Inject字段对于每个片段都是通用的,因此在BaseFragment中声明:

abstract class BaseFragment : DaggerFragment() {
    @Inject lateinit var vmFactory: ViewModelProvider.Factory
}

class DerivedFragmentA : BaseFragment()
class DerivedFragmentB : BaseFragment()
...

对于每个派生片段,我们必须手动创建模块 - 组件对,例如:

@Subcomponent
interface DerivedFragmentAComponent : AndroidInjector<DerivedFragmentA> {

    @Subcomponent.Builder
    abstract class Builder : AndroidInjector.Builder<DerivedFragmentA>()
}

@Module(subcomponents = [DerivedFragmentAComponent::class])
abstract class DerivedFragmentAModule {

    @Binds @IntoMap @FragmentKey(DerivedFragmentA::class)
    abstract fun bind(builder: DerivedFragmentAComponent.Builder): AndroidInjector.Factory<out Fragment>
}

并将它们安装到某个外部组件中,如下所示:

@Subcomponent(modules = [DerivedFragmentAModule::class, DerivedFragmentBModule::class, ...])
interface MainComponent : AndroidInjector<MainActivity> {

    @Subcomponent.Builder
    abstract class Builder : AndroidInjector.Builder<MainActivity>()
}

但这是一种样板。

如果我们尝试只为BaseFragment创建一个Module-Component并仅将其安装到MainComponent - 我们将在调用AndroidInjector.inject(fragment)方法期间获得运行时异常,并显示以下消息:

  "No injector factory bound for Class<DerivedFragmentA>. Injector factories were bound for supertypes of DerivedFragmentA: BaseFragment. Did you mean to bind an injector factory for the subtype?"

有没有办法解决这个问题并避免代码重复?或者Dagger-2强烈依赖于类名,impossible

1 个答案:

答案 0 :(得分:0)

使用Dagger 2进行注入始终适用于您指定的类型。 inject(fragment : BaseFragment)只会注入BaseFragment的字段,而不会注入任何子类中声明的字段。这只是你必须记住的事情。

您说您只想声明一个组件并仅将内容注入BaseFragment,这样您就可以 做什么。不是为DerivedFragment创建子组件,而是为BaseFragment ...

创建一个子组件
@Subcomponent
interface BaseFragmentComponent : AndroidInjector<BaseFragment> {

    @Subcomponent.Builder
    abstract class Builder : AndroidInjector.Builder<BaseFragment>()
}

然后,您可以将BaseFragment.Builder绑定到DerivedFragmentX

@Module(subcomponents = [BaseFragmentComponent::class])
abstract class BaseFragmentModule {

    @Binds @IntoMap @FragmentKey(DerivedFragmentA::class)
    abstract fun bind(builder: BaseFragmentComponent.Builder): AndroidInjector.Factory<out Fragment>

    @Binds @IntoMap @FragmentKey(DerivedFragmentB::class)
    abstract fun bind(builder: BaseFragmentComponent.Builder): AndroidInjector.Factory<out Fragment>

    @Binds @IntoMap @FragmentKey(DerivedFragmentC::class)
    abstract fun bind(builder: BaseFragmentComponent.Builder): AndroidInjector.Factory<out Fragment>
}

重要的是设置@FragmentKey(DerivedFragmentA::class)来引用子类,因为在调用AndroidInjection.inject(fragment)时,Dagger会查找一个。

我仍然建议你不要使用这种方法,因为你最终会混合使用完全注入的一些片段,而其他片段只是BaseFragment。这听起来让我感到困惑。

您可以使用@ContributesAndroidInjector代替为您生成样板代码并正确注入每个片段。