简体   繁体   中英

Dagger2 compilation error in MVVM Factory when defining injector for fragment

I have a next root component:

@Singleton
@Component(modules = [AndroidInjectionModule::class,
        AndroidSupportInjectionModule::class, 
        ActivityBuilderModule::class])
interface RootComponent : AndroidInjector<DaggerApplication> {
    fun inject(myApplication: MyApplication)
    override fun inject(photoPartyApplication: DaggerApplication)

    @Component.Builder
    interface Builder {
        @BindsInstance
        fun application(application: Application): Builder
        fun build(): RootComponent
    }
}

In ActivityBuilderModule:

@Module
abstract class ActivityBuilderModule {

    @ContributesAndroidInjector(modules = [MainActivityModule::class,
            ViewModelModule::class])
    @ActivityScope
    abstract fun bindMainActivity(): MainActivity

    @ContributesAndroidInjector(modules = [SecondaryActivityModule::class,
            ViewModelModule::class,
            FragmentBuilderModule::class])
    @ActivityScope
    abstract fun bindSecondaryActivity(): SecondaryActivity

}

ViewModelModule is a trivial module to help making constructor injections in ViewModel classes and consists of @Binds between specific instances and ViewModel type.

MainActivityModule and SecondaryActivityModule define specific dependencies for corresponding activities.

The key thing is that when I added this FragmentBuilderModule - compilation started to emit errors. The stack trace is the next:

error: [Dagger/MissingBinding] some_package.SpecificDependency cannot be provided without an @Inject constructor or an @Provides-annotated method.

public abstract interface RootComponent extends dagger.android.AndroidInjector {

^

A binding with matching key exists in component: some_package.ActivityBuilderModule_BindMainActivity.MainActivitySubcomponent

some_package.SpecificDependency is injected at some_package.MainViewModel(specificDependency, …)

some_package.MainViewModel is injected at some_package.ViewModelModule.mainViewModel(viewModel)

 Map<Class<? extends ViewModel>, Provider<ViewModel>> is injected at some_package.ViewModelFactory(viewModelProviders) 

some_package.ViewModelFactory is injected at some_package.ViewModelModule.bindViewModelFactory(factory)

android.arch.lifecycle.ViewModelProvider.Factory is injected at some_package.MyFragment.viewModelFactory

some_package.MyFragment is injected at dagger.android.AndroidInjector.inject(T)

[some_package.RootComponent → some_package.ActivityBuilderModule_BindSecondaryActivity.SecondaryActivitySubcomponent → some_package.FragmentBuilderModule_ProvideMyFragmentFactoryMyFragmentSubcomponent]

As far as I can understand, Dagger assumes that the whole dependencies graph has to be properly constructed for the map of Class<? extends ViewModel> -> Provider<ViewModel> Class<? extends ViewModel> -> Provider<ViewModel> , and if some ViewModels fall into factory , and that factory is injected into a component, then if component will ask for any viewmodel , it has to be delivered . And in order to deliver all the viewmodels , again, all the dependencies have to be available (what's not true, because specific dependency for the MainViewModel is available only from MainModule , and that's what dagger says before the stack trace).

Is there a workaround to provide dependencies to the map of Class<? extends ViewModel> -> Provider<ViewModel> Class<? extends ViewModel> -> Provider<ViewModel> on demand instead of building the whole graph at compile time (which is leading to the compile-time error)

First add ViewModelModule at RootComponent

 @Singleton
 @Component(modules = [AndroidInjectionModule::class,
            AndroidSupportInjectionModule::class, 
            ActivityBuilderModule::class,
            ViewModelModule::class])                 // add this so you don't have to add for every activity
    interface RootComponent : AndroidInjector<DaggerApplication> {
        fun inject(myApplication: MyApplication)
        override fun inject(photoPartyApplication: DaggerApplication)

        @Component.Builder
        interface Builder {
            @BindsInstance
            fun application(application: Application): Builder
            fun build(): RootComponent
        }
    }

Now in ActivityBuilderModule add only activty

 @Module
 abstract class ActivityBuilderModule {

    @ContributesAndroidInjector
    @ActivityScope
    abstract fun bindMainActivity(): MainActivity
 }

Now In ViewModelModule add all ViewModel

 @Module
 abstract class ViewModelModule {

    @Binds
    @IntoMap
    @ViewModelKey(MainActivityModule::class)
    abstract fun bindMainActivityViewModel(mainActivityViewModel: MainActivityModule): ViewModel
}

Add ViewModelKey

 @MustBeDocumented
 @kotlin.annotation.Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER)
 @kotlin.annotation.Retention(AnnotationRetention.RUNTIME)
 @MapKey
 internal annotation class ViewModelKey(val value: KClass<out ViewModel>)

And ViewModelFactory

@Singleton
class KotlinViewModelFactory @Inject
constructor(private val creators: Map<Class<out ViewModel>, @JvmSuppressWildcards Provider<ViewModel>>) : ViewModelProvider.Factory {

    @Suppress("UNCHECKED_CAST")
    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        var creator: Provider<out ViewModel>? = creators[modelClass]
        if (creator == null) {
            for ((key, value) in creators) {
                if (modelClass.isAssignableFrom(key)) {
                    creator = value
                    break
                }
            }
        }
        if (creator == null) {
            throw IllegalArgumentException("unknown model class $modelClass")
        }
        try {
            Timber.d(creator.toString())
            return creator.get() as T
        } catch (e: Exception) {
            throw RuntimeException(e)
        }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM