【问题标题】:Architecture Components ViewModels inject with dagger with params that come from the Activity/Fragment架构组件 ViewModel 使用来自 Activity/Fragment 的参数注入 dagger
【发布时间】:2018-02-17 13:13:40
【问题描述】:

我正在尝试将新的架构组件ViewModel 添加到我的应用程序中,同时用匕首注入它们。我的代码基于谷歌显示的here。我试图避免每个ViewModel 类型都有一个ViewModelFactory,所以我使用了接收Map<Class<? extends ViewModel>, Provider<ViewModel>> creatorsViewModelFactory。它适用于与@Singleton 范围具有依赖关系的ViewModels。但是,我的ViewModels 之一具有来自片段的依赖关系。这是那个片段的模块:

@Module
public abstract class DownloadIssueDialogFragmentModule {

    @Binds
    abstract DialogFragment dialogFragment(DownloadIssueDialogFragment dialogFragment);

    @Provides
    @FragmentScope
    static Issue provideIssue(DownloadIssueDialogFragment dialogFragment) {
        return dialogFragment.getIssue();
    }
}

还有我的ViewModelModule

@Module
public abstract class ViewModelModule {

    @Binds
    abstract ViewModelProvider.Factory bindViewModelFactory(ViewModelFactory factory);

    @Binds
    @IntoMap
    @ViewModelKey(DownloadIssueViewModel.class)
    abstract ViewModel bindDownloadIssueViewModel(DownloadIssueViewModel viewModel);

}

dagger 说它无法提供Issue。这是有道理的,因为Map<Class<? extends ViewModel>, Provider<ViewModel>> 似乎是在编译时创建的。但我只会在该片段的范围内知道参数。我怎样才能做到这一点?

谢谢。

编辑:

最后我采用了不同的方法。现在我为每个 ViewModel 创建一个工厂,而不是注入 ViewModel,而是注入工厂。

我创建了这个库:AutoViewModelFactory

自动生成工厂。这是迄今为止我找到的最好的解决方案。

【问题讨论】:

  • 你不能在同一个班级同时使用@Binds@Provides。您应该使用其中之一。仅供参考。
  • @mertsimsek 如果提供是静态的,您可以拥有@Binds@Provides。在尝试更改为 Google 之前,我使用自己的 ViewModels 进行了此操作(我需要在轮换中保留 ViewModels)。

标签: android mvvm dagger-2 dagger android-architecture-components


【解决方案1】:

由于 Android 架构组件 ViewModel 比 Fragment 具有更大的范围(读取更持久的生命周期),因此您应该避免让 ViewModel 依赖于 Fragment 中的字段。

但是,如果 Issue 仅在运行时已知并且由 Fragment 中的逻辑生成,则您可以通过使用 Holder 模式来避免小的依赖周期问题。

这已在 some other Dagger 2 StackOverflow questions 中讨论过,但您只需定义一个带有公共访问器/突变器的 Java bean:

class IssueHolder {
    private Issue issue;

    @Inject
    IssueHolder() {} //empty explicit constructor as required by Dagger 2         

    public void setIssue(@Nullable Issue issue) { 
        this.issue = issue;
    }

    @Nullable
    public Issue getIssue() {
        return issue;
    }
}

然后你可以让你的 ViewHolder 依赖于IssueHolder 而不是直接依赖于Issue

@Inject IssueHolder issueHolder;

public void doSomething() {
    if (issueHolder.get() == null) {
        throw new IllegalStateException("Expected IssueHolder to be set by IssueFragment at this point");
    }
    //TODO: the logic you want here
}

与任何模式一样,这种 Holder 模式应该谨慎使用,因为它很容易退化。如果可能的话,最好的解决方案是设计你的模块和依赖项,以消除循环的可能性,

【讨论】:

  • 感谢您的回答。我认为这个解决方案最终与从片段中获取对象后直接在 ViewModel 上设置对象相同。这个屏幕就像一个细节,我需要Issue 对象,它来自片段(传入它的参数)。我知道何时创建/检索 ViewModel 并且在概念上,我认为依赖项是正确的,我只是无法告诉 dagger 如何获取对象。
  • @RicardoCarrapiço 感谢您的支持。不知道是不是和直接在 ViewModel 上设置一模一样,因为可以通过控制对 Holder 的访问来控制 ViewModel 的哪些消费者可以设置。
猜你喜欢
  • 1970-01-01
  • 2021-02-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-01-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多