跨越两个/更多片段的共享ViewModel以及使用Dagger的活动

问题描述 投票:1回答:1

好吧,正如我试图在标题中总结的那样,这是详细信息。

我们有一个相对较大的应用程序,它实际上不是理想的方式使用Dagger,因此我们决定开始编写测试,为此,我需要公开Mockito的依赖项,因此,我面临着开始提供视图模型的问题。使用单例工厂,仍然适用,周围有大量的教程对此进行了解释。

我们在我们的应用程序中拥有许多功能,这些功能是使用单个活动和导航组件实现的,该单个活动有时具有创建的视图模型,可用于在容器活动和使用填充的片段之间共享数据导航编辑器。

我不知道以下内容,我如何使用匕首注入共享的视图模型,每次为特定视图模型调用@Inject时都返回相同的实例,我知道可以通过以下方法完成范围,但我无法弄清楚,我有一个解释,我需要验证。 (我将在下面提供我的代码)

我首先实现了Singleton ViewModelFactory,如下所示:

@Singleton
class ViewModelFactory @Inject constructor(private val creators: Map<Class<out ViewModel>,
        @JvmSuppressWildcards Provider<ViewModel>>) : ViewModelProvider.Factory {
    override fun <T : ViewModel?> create(modelClass: Class<T>): T {
        val creator = creators[modelClass] ?: creators.entries.firstOrNull {
            modelClass.isAssignableFrom(it.key)
        }?.value ?: throw IllegalArgumentException("unknown model class $modelClass")
        try {
            @Suppress("UNCHECKED_CAST")
            return creator.get() as T
        } catch (e: Exception) {
            throw RuntimeException(e)
        }
    }
}

然后我创建了如下的ViewModelModule,它提供了ViewModelFactory和ViewModel:

@Module
abstract class ViewModelFactoryModule {

    @Binds
    abstract fun bindsViewModelFactory(viewModelFactory: ViewModelFactory): ViewModelProvider.Factory

    @Binds
    @IntoMap
    @EbMainScope
    @ViewModelKey(EBMainContainerViewModel::class)
    abstract fun bindEbMainViewModel(ebMainContainerViewModel: EBMainContainerViewModel): ViewModel

}

在您询问之前,这是作用域的实现:

@Scope
@Target(
        AnnotationTarget.FUNCTION,
        AnnotationTarget.PROPERTY_GETTER,
        AnnotationTarget.PROPERTY_SETTER
)
@Retention(AnnotationRetention.RUNTIME)
annotation class EbMainScope

最后一步,这是我的活动/碎片注射器模块:

@Module
abstract class ScreensBuildersModule {

    @ContributesAndroidInjector
    @EbMainScope
    abstract fun contributeEbMainActivity(): EBMainActivity

    @ContributesAndroidInjector
    @EbMainScope
    abstract fun contributeEbDashboardMainFragment(): EBDashboardMainFragment

}

当然,我在AppComponent中进行了所有连接,尽管有我定义的范围,但应用程序运行顺利,有个问题,EbMainContainerViewModel有两个实例。

我的解释是,我实际上有两个不同的提供者而不是一个,但是我仍然不明白为什么,因为我将其标记为@Singleton

有人对此有解释吗?如果需要更多输入,请告诉我。

android dagger-2
1个回答
0
投票

然后,这是我设法完成的实用指南,我认为是可行的解决方案,并且由于@ pratz9999要求提供解决方案,所以这里是:

为了实例化ViewModel,您将需要一个ViewModelProvider,如果您依赖于上述实现,它会在后台创建一个ViewModelFactory,对于模块中的每个条目(即@IntoMap调用),都将实例化一个新的provider (可以),但是这里有个问题,它每次都会创建一个新的ViewModelFactory,请看以下内容:

/**
* Creates a {@link ViewModelProvider}, which retains ViewModels while a scope of given
* {@code fragment} is alive. More detailed explanation is in {@link ViewModel}.
* <p>
* It uses the given {@link Factory} to instantiate new ViewModels.
*
* @param fragment a fragment, in whose scope ViewModels should be retained
* @param factory  a {@code Factory} to instantiate new ViewModels
* @return a ViewModelProvider instance
*/
@NonNull
@MainThread
public static ViewModelProvider of(@NonNull Fragment fragment, @Nullable Factory factory) {
    Application application = checkApplication(checkActivity(fragment));
    if (factory == null) {
        factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
    }
    return new ViewModelProvider(fragment.getViewModelStore(), factory);
}

我在做过一些研究后猜测的错误是,我没有注入适当的ViewModelFactory,所以我最终做了以下事情:

  • 在我的基本片段类中,我按如下方式注入了ViewModelFactory:
/**
* Factory for injecting view models
*/
@Inject
lateinit var viewModelFactory: ViewModelProvider.Factory
  • 然后在一个实用工具类中,我有一个返回共享ViewModel的方法,如下所示(注意活动?运行这会使视图模型实例绑定到保持活动,因此具有共享作用域的概念):
fun <T: ViewModel> BaseNavFragmentWithDagger.getSharedViewModelWithParams(clazz: Class<T>): T =
        activity?.run { ViewModelProviders.of(this, viewModelFactory).get(clazz) }
                ?: throw RuntimeException("You called the view model too early")
  • 最后是我使用的私有ViewModel:
fun <T: ViewModel> BaseNavFragmentWithDagger.getPrivateViewModelWithParams(clazz: Class<T>): T =
        ViewModelProviders.of(this, viewModelFactory).get(clazz)
© www.soinside.com 2019 - 2024. All rights reserved.