问题描述
我已经开始使用Dagger2,所以还有很多东西要学习。 我想知道是否有人可以指出我正确的方向。
因此,我创建了一个模块,用于注册我的活动使用的视图模型。 看起来像这样:
@Module
abstract class ViewModelModule {
@Binds
internal abstract fun bindViewModelFactory(factory: ViewModelFactory): ViewModelProvider.Factory
@Binds
@IntoMap
@ViewModelKey(MainActivityViewModel::class)
internal abstract fun bindMainActivityViewModel(viewModel: MainActivityViewModel): ViewModel
@Binds
@IntoMap
@ViewModelKey(ShowDetailsViewModel::class)
abstract fun bindShowDetaislViewModel(viewModel: ShowDetailsViewModel): ViewModel
}
ViewModelKey
是一个简单的帮助程序注释类,如下所示:
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER)
@Retention(AnnotationRetention.RUNTIME)
@MapKey
annotation class ViewModelKey (val value: KClass<out ViewModel>) {
}
ViewModelModule
由我的主要应用程序组件加载(用于创建应用程序):
@Singleton
@Component(
modules=[
AndroidSupportInjectionModule::class,
AppModule::class,
DatabaseModule::class,
NewsServiceModule::class,
JobBindingModule::class,
ViewModelModule::class,
PreferencesModule::class,
ActivityBindingModule::class
]
)
interface AppComponent: AndroidInjector<MyApp> {
@Component.Builder
abstract class Builder: AndroidInjector.Builder<MyApp>()
}
这是ActivityBindingModule
的代码,负责设置子组件(在本例中为我的应用程序使用的活动):
@Module
abstract class ActivityBindingModule {
@ActivityScoped
@ContributesAndroidInjector()
internal abstract fun mainActivity(): MainActivity
@ActivityScoped
@ContributesAndroidInjector
internal abstract fun showDetailsActivity(): ShowDetailsActivity
}
在内部,每个活动都使用如下所示的代码实例化视图模型(从onCreate
方法中调用):
//view model code
_viewModel = ViewModelProviders.of(this, viewModelFactory)[ShowDetailsViewModel::class.java]
而且,正如您所期望的, viewModelFactory
被注入为字段:
@Inject lateinit var viewModelFactory: ViewModelProvider.Factory
这两个视图模型都具有外部依赖关系,这些依赖关系在顶级应用程序组件引用的其他模块上进行设置。
并且,出于完整性考虑,以下是我的视图模型工厂的代码:
@Singleton
class ViewModelFactory @Inject constructor(private val viewModels: MutableMap<Class<out ViewModel>, Provider<ViewModel>>) : ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T
= viewModels[modelClass]?.get() as T
该代码有效,但似乎可以改进。
阅读文档后,我的印象是我可以重构ViewModeModule
以便它可以简单地实例化ViewModelFactory
并将每个视图模型声明移动到单独的模块中(这样,每个视图模型声明只能在“正确的“活动)。
为了测试这一点,我首先将ShowDetailsViewModel
移到一个只有一个条目的新模块中:
@Module
internal abstract class DetailsModule {
@Binds
@IntoMap
@ViewModelKey(ShowDetailsViewModel::class)
abstract fun bindShowDetaislViewModel(viewModel: ShowDetailsViewModel): ViewModel
}
之后,ViewModelModule如下所示:
@Module
abstract class ViewModelModule {
@Binds
internal abstract fun bindViewModelFactory(factory: ViewModelFactory): ViewModelProvider.Factory
@Binds
@IntoMap
@ViewModelKey(MainActivityViewModel::class)
internal abstract fun bindMainActivityViewModel(viewModel: MainActivityViewModel): ViewModel
}
而且我已经更新了ActivityBindingModule
使它看起来像这样:
@Module
abstract class ActivityBindingModule {
@ActivityScoped
@ContributesAndroidInjector()
internal abstract fun mainActivity(): MainActivity
@ActivityScoped
@ContributesAndroidInjector(modules = [DetailsModule::class])
internal abstract fun showDetailsActivity(): ShowDetailsActivity
}
请注意,现在我将DetailsModule
(实例化ShowDetailsViewModel
)传递给ContributeAndroidInjector
批注,该批注将应用于showDetailsActivity
方法,因为该视图模型仅由该活动使用。
现在,我肯定会丢失一些东西,因为这样做之后,我总是会遇到以下异常:
java.lang.IllegalStateException: ViewModelProviders.of(th…ilsViewModel::class.java] must not be null
如果我调试该应用程序,则可以看到将ShowDetailsViewModel移入其自己的模型中并不会在工厂使用的地图上注册它(即,该地图只有一个条目,它对应于在ViewModelModule中注册的MainActivityViewModel
。
我认为将每个视图模型的声明移动到子组件使用的每个模块中,仍应允许其在由顶部组件注册的模块注入的映射中注册。 我错了吗? 使我无法进行这项工作是什么?
谢谢。
1楼
问题在于ViewModelFactory
是@Singleton
,它不会获得您在子组件中添加的任何绑定。
从工厂中删除范围,或将其设置为@ActivityScoped
(与Activity的ViewModel相同的范围)
活动( @ActivityScoped
)有权访问工厂( @Singleton
),但是工厂( @Singleton
)无权使用或从较低范围( @ActivityScoped
)创建ViewModel。
因此,将工厂移至同一作用域( @ActivityScoped
)将使它具有访问权限以创建有问题的视图模型。