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.