当前位置: 代码迷 >> Android >> dagger2和android:加载模块,将视图模型注入地图
  详细解决方案

dagger2和android:加载模块,将视图模型注入地图

热度:74   发布时间:2023-08-04 12:38:38.0

我已经开始使用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

我认为将每个视图模型的声明移动到子组件使用的每个模块中,仍应允许其在由顶部组件注册的模块注入的映射中注册。 我错了吗? 使我无法进行这项工作是什么?

谢谢。

问题在于ViewModelFactory@Singleton ,它不会获得您在子组件中添加的任何绑定。 从工厂中删除范围,或将其设置为@ActivityScoped (与Activity的ViewModel相同的范围)

活动( @ActivityScoped )有权访问工厂( @Singleton ),但是工厂( @Singleton )无权使用或从较低范围( @ActivityScoped )创建ViewModel。 因此,将工厂移至同一作用域( @ActivityScoped )将使它具有访问权限以创建有问题的视图模型。

  相关解决方案