无法使用 Dagger2 将对象注入 ViewModel 类

Posted

技术标签:

【中文标题】无法使用 Dagger2 将对象注入 ViewModel 类【英文标题】:Can't inject object into ViewModel class using Dagger2 【发布时间】:2022-01-15 21:45:22 【问题描述】:

我学习了 Dagger2 并尝试使用 MVVM 制作应用程序。我根据方案创建了 Dagger 类(Modules、Components、BaseApplication),但是当我尝试将 QuotableAPI 对象注入 ViewModel 时,Dagger 不会生成 DaggerAppComponent 类(在 ViewModel 构造函数中没有 QuotableAPI 生成)。

QuotesViewModel

public class QuotesViewModel extends ViewModel 

    private static final String TAG = "QuotesViewModel:";
    private QuotableAPI quotableApi;


    @Inject
    public QuotesViewModel(QuotableAPI quotableAPI) 
        this.quotableApi = quotableAPI;

        Log.d(TAG, "QuotesViewModel created...");
    

AppModule

@Module
public class AppModule 

    @Singleton
    @Provides
    static Retrofit provideRetrofitInstance()
        return new Retrofit.Builder()
                .baseUrl(BASE_URL)
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .addConverterFactory(GsonConverterFactory.create())
                .build();
    

    @Singleton
    @Provides
    static QuotableAPI provideQuotableApi(Retrofit retrofit)
        return retrofit.create(QuotableAPI.class);
    

应用组件

@Component(
        modules = 
                androidSupportInjectionModule.class,
                ActivityBuildersModule.class,
                AppModule.class,
                ViewModelFactoryModule.class
        
)
public interface AppComponent extends AndroidInjector<BaseApplication> 

        @Component.Builder
        interface Builder

                @BindsInstance
                Builder application(Application application);

                AppComponent build();
        


基础应用程序

public class BaseApplication extends DaggerApplication 
    @Override
    protected AndroidInjector<? extends DaggerApplication> applicationInjector() 
        return DaggerAppComponent.builder().application(this).build();
    

ViewModelFactory

public class ViewModelFactory implements ViewModelProvider.Factory 

    private static final String TAG = "ViewModelProviderFactor";

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

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

    @Override
    public <T extends ViewModel> T create(Class<T> modelClass) 
        Provider<? extends ViewModel> creator = creators.get(modelClass);
        if (creator == null)  // if the viewmodel has not been created

            // loop through the allowable keys (aka allowed classes with the @ViewModelKey)
            for (Map.Entry<Class<? extends ViewModel>, Provider<ViewModel>> entry : creators.entrySet()) 

                // if it's allowed, set the Provider<ViewModel>
                if (modelClass.isAssignableFrom(entry.getKey())) 
                    creator = entry.getValue();
                    break;
                
            
        

        // if this is not one of the allowed keys, throw exception
        if (creator == null) 
            throw new IllegalArgumentException("unknown model class " + modelClass);
        

        // return the Provider
        try 
            return (T) creator.get();
         catch (Exception e) 
            throw new RuntimeException(e);
        
    

ViewModelFactoryModule

@Module
public abstract class ViewModelFactoryModule 

    @Binds
    public abstract ViewModelProvider.Factory bindViewModelFactory(ViewModelFactory viewModelFactory);

ViewModelKey

@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@MapKey
public @interface ViewModelKey 
    Class<? extends ViewModel> value();

ViewModelModule

@Module
abstract class ViewModelModule 

    @Binds
    @IntoMap
    @ViewModelKey(QuotesViewModel.class)
    public abstract ViewModel bindQuotesViewModel(QuotesViewModel viewModel);

和我的依赖:

implementation 'com.google.dagger:dagger:2.35.1'
annotationProcessor 'com.google.dagger:dagger-compiler:2.24'
implementation 'com.google.dagger:dagger-android:2.35.1'
implementation 'com.google.dagger:dagger-android-support:2.24'
annotationProcessor 'com.google.dagger:dagger-android-processor:2.24'

在 QuotesViewModel 构造函数中没有任何东西,一切都很好,但是当我添加任何参数时,我得到了这个错误:

C:\Users\ceran\Desktop\Projekty\Android\BestQuotesApp-dagger\app\src\main\java\com\example\bestquotesapp\di\AppComponent.java:21: error: [Dagger/IncompatiblyScopedBindings] com.example.bestquotesapp.di.AppComponent (unscoped) may not reference scoped bindings:
public interface AppComponent extends AndroidInjector<BaseApplication> 
       ^
      @Singleton @Provides com.example.bestquotesapp.network.QuotableAPI com.example.bestquotesapp.di.AppModule.provideQuotableApi(retrofit2.Retrofit)
      @Singleton @Provides retrofit2.Retrofit com.example.bestquotesapp.di.AppModule.provideRetrofitInstance()

还有其他错误,当我再次重建项目时:

C:\Users\ceran\Desktop\Projekty\Android\BestQuotesApp-dagger\app\src\main\java\com\example\bestquotesapp\BaseApplication.java:7: error: cannot find symbol
import com.example.bestquotesapp.di.DaggerAppComponent;
                                   ^
  symbol:   class DaggerAppComponent
  location: package com.example.bestquotesapp.di

【问题讨论】:

能否也附上QuotesViewModel使用的地方的代码(可能是活动或片段)? 您的ViewModelFactory 通过@ViewModelKey 注释创建ViewModel 实例,但您没有在代码中包含提供程序方法。它只是从您的帖子中丢失,还是从您的代码中丢失? viewModel = new ViewModelProvider(requireActivity(), viewModelFactory).get(QuotesViewModel.class); 这就是我在片段中初始化 QuotesViewModel 的方式 @ViewModelKey 注释仅在我的帖子中丢失,我的代码中有它 【参考方案1】:

QuotableAPI 是您改造路线的接口吗?如果是这样,您将无法提供这样的界面。一种选择是创建一个QuotableAPIClient 类来注入改造实例。

public class QuotableAPIClient 

  private QuotableAPI quotableApi;

  @Inject
  public QuotableAPIClient(Retrofit retrofit) 
    quotableApi = retrofit.create(QuotableAPI.class);
  



然后你将这个客户端注入你的QuotesViewModel

public class QuotesViewModel extends ViewModel 

    private static final String TAG = "QuotesViewModel:";
    private QuotableAPIClient quotableApiClient;


    @Inject
    public QuotesViewModel(QuotableAPIClient quotableApiClient) 
        this.quotableApiClient = quotableApiClient;

        Log.d(TAG, "QuotesViewModel created...");
    

【讨论】:

还是一样。而且我不能注入任何其他对象。 OP 使用@Provides 方法提供接口,这对我来说似乎很好,并且与将其包装在客户端类中相比不会有什么不同。

以上是关于无法使用 Dagger2 将对象注入 ViewModel 类的主要内容,如果未能解决你的问题,请参考以下文章

Dagger2:无法在 WorkManager 中注入依赖项

dagger2简单使用与理解笔记

dagger2简单使用与理解笔记

Dagger2注入原理解析

应该将演示者(mvP)注入(dagger2)到android中的视图吗?

Android单排上王者系列之Dagger2注入原理解析