【问题标题】:Dagger - MissingBinding Map<Class<? extends ViewModel>,Provider<ViewModel>> cannot be provided without an @Provides-annotated methodDagger - MissingBinding Map<Class<? extends ViewModel>,Provider<ViewModel>> 不能在没有 @Provides 注释的方法的情况下提供
【发布时间】:2019-09-16 10:13:47
【问题描述】:

我刚开始使用 Dagger 框架进行依赖注入,所以我可能遗漏了一些琐碎的东西,但我花了 2 天多的时间来复习代码和教程。

所以,我正在尝试为我的ViewModels 使用 dagger 依赖注入,我发现我可以使用我已经实现的称为多重绑定的功能来做到这一点。我有一个AppComponent 和一个ActivitySubComponentAppComponent 安装了 ViewModelFactoryModule 其中 @BindsViewModelProvider.Factory,我希望在应用程序中使用这个 Module,因为我认为它应该独立于 Activity 生命周期。在我的ActivitySubComponent 中,我有一个ViewModelsModule,其中@Binds 一个ViewModel

现在,据我了解,我已将 ActivitySubComponent 包含在 ViewModelFactoryModule 中,这会将子组件添加到 AppComponent

编辑:

现在,当我制作/构建项目时,我收到以下错误。抱歉,原来的错误是:

error: [Dagger/MissingBinding] java.util.Map<java.lang.Class<? extends androidx.lifecycle.ViewModel>,javax.inject.Provider<androidx.lifecycle.ViewModel>> cannot be provided without an @Provides-annotated method.
java.util.Map<java.lang.Class<? extends androidx.lifecycle.ViewModel>,javax.inject.Provider<androidx.lifecycle.ViewModel>> is injected at
com.mobile.presentation.viewmodels.build.ViewModelFactory(creators)
com.mobile.presentation.viewmodels.build.ViewModelFactory is provided at
com.mobile.app.AppComponent.obtainViewModelsFactory()

AppComponent.javaMap&lt;Class&lt;? extends ViewModel&gt;, Provider&lt;ViewModel&gt;&gt; obtainMap();添加如下方法后出现如下错误。

error: [Dagger/MissingBinding] java.util.Map<java.lang.Class<? extends androidx.lifecycle.ViewModel>,javax.inject.Provider<androidx.lifecycle.ViewModel>> cannot be provided without an @Provides-annotated method.
java.util.Map<java.lang.Class<? extends androidx.lifecycle.ViewModel>,javax.inject.Provider<androidx.lifecycle.ViewModel>> is provided at
com.mobile.app.AppComponent.obtainMap()
It is also requested at:
com.mobile.presentation.viewmodels.build.ViewModelFactory(creators)
The following other entry points also depend on it:
com.mobile.app.AppComponent.obtainViewModelsFactory()

jibber jabber 够了,下面是相关代码:

AppComponent.java

@AppScope
@Component(
        modules = {
                AppModule.class,
                ViewModelFactoryModule.class
        })
public interface AppComponent {
    App obtainApp();

    ViewModelFactory obtainViewModelsFactory();

    // I tried with adding the following line, out of sheer annoyance.
    // Map<Class<? extends ViewModel>, Provider<ViewModel>> obtainMap();
}

ViewModelFactoryModule.java

@Module(subcomponents = ActivitySubComponent.class)
public abstract class ViewModelFactoryModule {
    @AppScope
    @Binds
    public abstract ViewModelProvider.Factory bindViewModelFactory(ViewModelFactory viewModelFactory);
}

ViewModelsFactory.java

@AppScope
public class ViewModelFactory implements ViewModelProvider.Factory {

    private final Map<Class<? extends ViewModel>, Provider<ViewModel>> creators;

    @Inject
    public ViewModelFactory(Map<Class<? extends ViewModel>, Provider<ViewModel>> creators) {
        this.creators = creators;
    }

    @SuppressWarnings("unchecked")
    @NonNull
    @Override
    public <T extends ViewModel> T create(@NonNull Class<T> modelClass)
    {
        Provider<? extends ViewModel> creator = creators.get(modelClass);
        if (creator == null) {
            for (Map.Entry<Class<? extends ViewModel>, Provider<ViewModel>> entry : creators.entrySet()) {
                if (modelClass.isAssignableFrom(entry.getKey())) {
                    creator = entry.getValue();
                    break;
                }
            }
        }

        if (creator == null) {
            throw new IllegalArgumentException("unknown model class " + modelClass);
        }

        try {
            return (T) creator.get();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

ActivitySubComponent.java

// I have a separate module for my `LiveData`.
@ActivityScope
@Subcomponent(
        modules = {
                ActivityModule.class,
                ViewModelsModule.class,
                SplashLiveDataModule.class
        }
)
public interface ActivitySubComponent {

    Activity obtainActivity();

    LiveData<InitialAPIResponse> obtainLiveInitialApiResponse();

    LiveData<InitialSplashApiStatus> obtainLiveInitialSplashApiStatus();

    @Subcomponent.Builder
    interface Builder {
        ActivitySubComponent build();

        Builder setActivityModule(ActivityModule activityModule);
    }
}

ViewModelsModule.java

@Module
public abstract class ViewModelsModule {

    @ActivityScope
    @Binds
    @IntoMap
    @ViewModelKey(SplashViewModel.class)
    public abstract ViewModel provideVideoListViewModel(SplashViewModel viewModel);
}

现在,我从中了解到AppComponent 不能直接访问ActivitySubComponent,但它应该能够访问SubComponent,因为@Subcomponent 是作为组件的内部类生成的(也许我遗漏了一些东西),而且我认为 ViewModels 不应该绑定到应用程序的组件(我这样说是因为如果我将 ViewModelsModule 也移动到 AppComponent 我可以运行代码并且一切正常对),我的逻辑方法错了吗?

匕首依赖:

implementation 'com.google.dagger:dagger:2.22'
annotationProcessor 'com.google.dagger:dagger-compiler:2.22'

【问题讨论】:

标签: java android viewmodel dagger-2 android-viewmodel


【解决方案1】:

你是对的。在ViewModelFactoryModule 中添加ActivitySubComponent 会将子组件添加到AppComponent。

当您想将子组件添加到父组件时,您有两种方法。 首先通过在模块中添加子组件,然后将该模块添加到父模块。其次,通过在父组件中声明一个返回子组件的抽象方法。

您的第一部分是正确的,但是在您的AppComponent 中,您创建了一个返回 ViewModelFactory 的方法,该方法已由 ViewModelFactoryModule 提供。这与对象图冲突,因此您的构建失败。您需要删除返回 ViewModelFactory 和 Map 的两个方法,这将解决问题。

【讨论】:

  • 与其自己创建子组件并编写大量样板代码,不如开始使用 dagger-android 依赖项,以便为 android 注入依赖项。试试 Mitch 的这门课程(codingwithmitch.com/courses/dagger22-android)。这将有助于理解如何以最少的编码实现 android 中的 di。
  • 嗯...我明白为什么组件不能依赖Subcomponent。但是,这仍然没有回答问题,那么您如何在组件之间拆分工作? ViewModelFactory 显然必须与 AppComponent 绑定,否则它将创建新工厂,并在每个 Activity 中创建新的 ViewModels,只要启动 ActivityComponent
  • 我从您的评论中看到,Dagger-Android 的链接。但正如问题中所述,我正在学习如何使用 Dagger,并且希望至少深入研究一次样板代码,以便了解事情应该如何工作。
  • GoogleSample (github.com/googlesamples/android-architecture-components/tree/…) 有一个使用 dagger-android 进行依赖注入的示例应用程序。您可以在您的应用中使用它。
猜你喜欢
  • 2019-09-04
  • 2020-06-06
  • 1970-01-01
  • 1970-01-01
  • 2019-11-06
  • 2021-05-05
  • 2021-06-11
  • 1970-01-01
相关资源
最近更新 更多