Dagger 2 ContributesAndroidInjector обеспечивает активность модуля

Я пытаюсь найти способ менее конструктивного использования ActivityModule, который используется во всех моих приложениях. Это моя текущая настройка:

ActivityModule

@Module
class ActivityModule(private val activity: Activity) {

    @Provides @ActivityScope
    fun providesActivity(): Activity = activity

    @Provides @ActivityContext @ActivityScope
    fun providesContext(): Context = activity

    @Provides @ActivityContext @ActivityScope
    fun providesLayoutInflater(): LayoutInflater = activity.layoutInflater

    @Provides @ActivityContext @ActivityScope
    fun providesResources(): Resources = activity.resources

}

AppActivityModule (предоставляет действия для AndroidInjectionModule)

@Module(subcomponents = [
        AppActivityModule.WelcomeActivityComponent::class
    ])
    internal abstract class AppActivityModule {

        @Binds 
        @IntoMap 
        @ActivityKey(WelcomeActivity::class)
        abstract fun bindWelcomeActivityInjectorFactory(builder: WelcomeActivityComponent.Builder): AndroidInjector.Factory<out Activity>

        @ActivityScope
        @Subcomponent(modules = [(ActivityModule::class)])
        interface WelcomeActivityComponent : AndroidInjector<WelcomeActivity> {
        @Subcomponent.Builder abstract class Builder : AndroidInjector.Builder<WelcomeActivity>() {
            abstract fun activityModule(myActivityModule: ActivityModule): AndroidInjector.Builder<WelcomeActivity>

            override fun seedInstance(instance: WelcomeActivity) {
                activityModule(ActivityModule(instance))
            }
        }
    }
}

Вместо этого я хочу, чтобы AppActivityModule был:

@Module
internal abstract class AppActivityModule {
    @ContributesAndroidInjector(modules = [(ActivityModule::class)])
    abstract fun contributeWelcomeActivityInjector(): WelcomeActivity
}

Но это, довольно понятно, дает мне ошибку /di/AppActivityModule_ContributeWelcomeActivityInjector.java:29: error: @Subcomponent.Builder is missing setters for required modules or subcomponents: [...di.modules.ActivityModule]

Мой вопрос: есть ли более способный способ достичь того, что я пытаюсь сделать? Я знаю о @Bind и @BindsInstance (от этот ответ), но это, похоже, работает только в том случае, если у меня есть модульное действие и связываю конкретный вид активности, который Я не хочу в этом случае - я хочу, чтобы ActivityModule работал со всеми действиями.

Ответ 1

Один из способов минимизировать шаблон - создать общий модуль ActivityModule, а затем создать небольшой конкретный модуль для каждого действия. Прости мой неопытность Kotlin, но здесь идет:

// Abstract class so you don't have to provide an instance
@Module
abstract class ActivityModule {

    // No need for ActivityScope: You're always binding to the same Activity, so
    // there no reason to have Dagger save your Context instance in a Provider.
    @Binds @ActivityContext
    abstract fun providesContext(activity: Activity): Context

    // This does not *have* to be in a companion object, but that way
    // Android can do a static dispatch instead of a virtual method dispatch.
    // If you don't need that, just skip the constructor arguments and make these
    // normal methods and you'll be good to go.
    @Module
    companion object {
        @JvmStatic @Provides @ActivityContext
        fun providesLayoutInflater(activity: Activity): LayoutInflater = 
            activity.layoutInflater

        @JvmStatic @Provides @ActivityContext
        fun providesResources(activity: Activity): Resources = activity.resources
    }
}

И ваш модуль:

@Module
internal abstract class AppActivityModule {

    @Module
    internal interface WelcomeActivityModule {
      // The component that @ContributesAndroidInjector generates will bind
      // your WelcomeActivity, but not your Activity. So just connect the two,
      // and suddenly you'll have access via injections of Activity.
      @Binds fun bindWelcomeActivity(activity: WelcomeActivity) : Activity
    }

    @ContributesAndroidInjector(
        modules = [ActivityModule::class, WelcomeActivityModule::class])
    abstract fun contributeWelcomeActivityInjector(): WelcomeActivity
}

Обратите внимание, что хотя это работает для Activity, Service, BroadcastReceiver и других, вы, возможно, не захотите быть настолько быстрыми для Fragment. Это связано с тем, что dagger.android обрабатывает иерархии фрагментов с родительскими фрагментами, поэтому из дочернего компонента вы можете получить доступ к YourApplication, YourActivity, YourParentFragment и YourChildFragment, а также ко всем их компонентам. Если что-то в YourChildFragmentComponent зависит от неквалифицированного фрагмента, было бы неоднозначно, действительно ли оно хочет YourParentFragment или YourChildFragment. Тем не менее, этот дизайн имеет смысл для действий и определенных фрагментов, поэтому имеет смысл использовать его (осторожно).


ОБНОВЛЕНИЕ: Что здесь делает @ActivityContext?

@ActivityContext здесь вы можете определить квалификационную аннотацию, которую вы можете использовать для различения привязок того же типа в Dagger и других DI-средах, предположительно @ApplicationContext Context и @ActivityContext Context. Было бы неплохо не попробовать его, но я настоятельно рекомендую сохранить его и избегать привязки неквалифицированного контекста: контексты приложения и действия могут отличаться, особенно в многоэкранных или автоматических средах, и для получения нужных ресурсов и данных, которые вы должен быть точным, о котором вы используете. Вы можете использовать этот в качестве примера.