Androidew1267
Androidew1267

Reputation: 643

Can't inject object into ViewModel class using Dagger2

I learn Dagger2 and try to make app using MVVM. I created Dagger classes (Modules, Components, BaseApplication) according to the scheme, but when I try to inject QuotableAPI object into ViewModel, Dagger doesn't generate DaggerAppComponent class (which is generated without QuotableAPI in ViewModel constructor).

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);
    }
}

AppComponent

@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();
        }

}

BaseApplication

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);
}

and my dependencies:

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'

Without anything in QuotesViewModel constructor, everything is fine, but when I add any argument I got this error:

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()

And other error, when I rebuild project once again:

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

Upvotes: 0

Views: 394

Answers (1)

Stephen Vinouze
Stephen Vinouze

Reputation: 2085

Is QuotableAPI an interface for your Retrofit routes? If so, you cannot provide an interface like this. One option would be to create a QuotableAPIClient class that would inject the retrofit instance.

public class QuotableAPIClient {

  private QuotableAPI quotableApi;

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

}

Then you'd inject this client into your 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...");
    }
}

Upvotes: 0

Related Questions