Reputation: 643
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
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