希望日复一日研究这个非常有趣的主题“继承的子组件multibindings
,您可以在这里找到Inherited subcomponent multibindings,这是该页面的最后一个主题。
根据官方文档:
subComponent
可以将元素添加到其父级绑定的multibound
集或映射中。发生这种情况时,集合或映射将根据注入的位置而有所不同。当将其注入到subcomponent
上定义的绑定中时,它具有由子组件的multibindings
定义的值或条目以及由父组件的multibindings
定义的值或条目。当将其注入到在父组件上定义的绑定中时,它仅具有在那里定义的值或条目。
换句话说。如果父Component
有一个multibound set or map
且一个child component
已绑定到该多绑定,则这些绑定将链接/添加到父映射中,具体取决于这些绑定在匕首作用域中的注入位置如果有的话。
在Android应用程序中使用dagger version 2.24
使用Kotlin
。我有一个ApplicationComponent
使用了新的@Component.Factory
方法。 ApplicationComponent已安装AndroidSupportInjectionModule
。
我也有一个使用新ActivitySubComponent
方法的@Component.Factory
,并且使用Module注释的subComponents
参数将该方法链接到AppComponent。
这个ActivitySubComponent通过这样的绑定提供了ViewModel
@Binds
@IntoMap
@ViewModelKey(MyViewModel::class)
fun provideMyViewModel(impl: MyViewModel): ViewModel
@ViewModelKey
是自定义的匕首注释。
我也有一个这样实现的ViewModelFactory。
@Singleton
class ViewModelFactory @Inject constructor(
private val viewModelsToInject: Map<Class<out ViewModel>, @JvmSuppressWildcards Provider<ViewModel>>
) : ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T =
viewModelsToInject[modelClass]?.get() as T
}
普通的ViewModelFactory
这里的区别是,我在AppComponents模块之一中提供了此ViewModelFactory。但是ActivitySubComponent中的绑定viewModel不会添加到AppComponent的ViewModelFactory地图中。
换句话说。该文档所描述的内容根本没有发生。
如果将viewModels绑定移动到任何AppComponent模块中,则所有工作正常。
你知道这里会发生什么吗?
答案 0 :(得分:1)
您将ViewModelProvider.Factory
的范围定为@Singleton
。这样可以确保将其创建并保留在@Singleton
组件中。
删除范围是安全的,因为它不保留任何状态,并且可以使用正确的绑定集在需要的地方创建工厂。
答案 1 :(得分:1)
文档是准确的。尽管Dagger确实按照生成Set / Map Multibindinds时所描述的方式进行操作,但由于您处于极端情况,因此Dagger的工作方式有所不同。
假设您具有以下模块:
/**
* Binds ViewModelFactory as ViewModelProvider.Factory.
*/
@Module
abstract class ViewModelProviderModule {
@Binds abstract fun bindsViewModelFactory(impl: ViewModelFactory): ViewModelProvider.Factory
}
/**
* For the concept, we bind a factory for an AppViewModel
* in a module that is included directly in the AppComponent.
*/
@Module
abstract class AppModule {
@Binds @IntoMap
@ViewModelKey(AppViewModel::class)
abstract fun bindsAppViewModel(vm: AppViewModel): ViewModel
}
/**
* This module will be included in the Activity Subcomponent.
*/
@Module
abstract class ActivityBindingsModule {
@Binds @IntoMap
@ViewModelKey(MyViewModel::class)
}
/**
* Generate an injector for injecting dependencies that are scoped to MyActivity.
* This will generate a @Subcomponent for MyActivity.
*/
@Module
abstract class MyActivityModule {
@ActivityScoped
@ContributesAndroidInjector(modules = [ActivityBindingsModule::class])
abstract fun myActivity(): MyActivity
}
如果要将ViewModelProvider.Factory
注入应用程序类,那么Map<Class<out ViewModel>, Provider<ViewModel>>
应该提供什么?由于您要插入AppComponent
的范围,因此该ViewModelFactory
将只能创建AppViewModel
的实例,而不能创建MyViewModel
的实例,因为绑定是在子组件中定义的。
如果您在ViewModelProvider.Factory
中注入MyActivity
,则由于我们都在AppComponent
和MyActivitySubcomponent
的范围内,因此将新建一个ViewModelFactory
能够创建AppViewModel
和MyViewModel
的两个实例。
这里的问题是 ViewModelFactory
被标注为@Singleton
。因此,将创建ViewModelFactory的单个实例并将其保存在AppComponent
中。由于MainActivityComponent
是AppComponent
的子组件,因此它将继承该单例,并且不会创建包含具有2个ViewModel
绑定的Map的新实例。
以下是发生的一系列事情:
MyApplication.onCreate()
被调用。您创建了DaggerAppComponent
。DaggerAppComponent
的构造函数中,Dagger构建了一个Map,该映射具有从Class<AppViewModel>
到Provider<AppViewModel>
的映射。ViewModelFactory
的依赖项,然后将其保存在组件中。ViewModelFactory
的引用,然后直接注入(它不会修改地图)。@Singleton
上的ViewModelFactory
注释。这样可以确保Dagger每次需要时都创建一个ViewModelFactory
的新实例。这样,ViewModelFactory
将收到包含两个绑定的Map。@Singleton
上的ViewModelFactory
注释替换为@Reusable
。这样,Dagger将尝试重用ViewModelProvider
的实例,而不能保证在整个应用程序中使用唯一的实例。如果检查生成的代码,则会注意到AppComponent
和MyActivitySubcomponent
的每个实例中都保留了一个不同的实例。答案 2 :(得分:1)
这是因为在AppComponent中创建了地图,并且您正在将ViewModel添加到子组件中的地图。换句话说,当应用启动时,它会使用ViewModelFactory
创建地图。但是MyViewModel
不会添加到地图,因为它存在于子组件中。
我为此苦苦挣扎了好几天,当您说匕首文档对它的概述不太好时,我同意了。直观上,您认为AppComponent中声明的依赖项可用于所有子组件。 但这不适用于Map Multibindings。或至少不是完全正确。 MyViewModel
未添加到地图,因为创建它的工厂位于AppComponent内部。
无论如何,我最终实现的解决方案是我创建了特定于功能的ViewModelFactory
。因此,对于每个子组件,我创建了一个ViewModelFactory
,它具有自己的 Key 和一组多重绑定。
我制作了一个示例存储库,您可以查看:https://github.com/mitchtabian/DaggerMultiFeature/
签出分支:“ feature-specific-vm-factories” 。我将确保按原样离开该分支,但将来可能会更改主分支。