2

I am using Dagger 2 but getting this strange error, nothing seems not OK so far to me but clearly something is missing. Any help would be appreciated.

Error:(16, 8) error: [dagger.android.AndroidInjector.inject(T)] com.example.user.viewmodel.TripViewModel cannot be provided without an @Inject constructor or from an @Provides-annotated method. This type supports members injection but cannot be implicitly provided.
com.example.user.viewmodel.TripViewModel is injected at
com.example.user.di.ViewModelModule.bindTripViewModel(tripViewModel)
java.util.Map<java.lang.Class<? extends android.arch.lifecycle.ViewModel>,javax.inject.Provider<android.arch.lifecycle.ViewModel>> is injected at
com.example.user.di.TripViewModelFactory.<init>(creators)
com.example.user.di.TripViewModelFactory is injected at
com.example.user.di.ViewModelModule.bindViewModelFactory(factory)
android.arch.lifecycle.ViewModelProvider.Factory is injected at
com.example.user.fragment.HomeFragment.viewModelFactory
com.example.user.fragment.HomeFragment is injected at
dagger.android.AndroidInjector.inject(arg0)

Here are the codes:

AppModule.java

@Module(includes = ViewModelModule.class)
class AppModule {
    @Singleton
    @Provides
    MyRoomDatabase providesRoomDatabase(Application application) {
        return Room.databaseBuilder(application,
                MyRoomDatabase.class, "name_db").build();
    }

    @Singleton
    @Provides
    TripRepository provideTripRepository(TripDao tripDao){
        return new TripRepository(tripDao);
    }

    @Singleton
    @Provides
    TripDao provideTripDao(MyRoomDatabase myRoomDatabase){
        return myRoomDatabase.getTripDao();
    }

}

ViewModelModule.java

@Module
public abstract class ViewModelModule {
    @Binds
    @IntoMap
    @ViewModelKey(TripViewModel.class)
    abstract ViewModel bindTripViewModel(TripViewModel tripViewModel);

    @Binds
    abstract ViewModelProvider.Factory bindViewModelFactory(TripViewModelFactory factory);
}

MainActivityModule.java

@Module
public abstract class MainActivityModule {
    @ContributesAndroidInjector(modules = FragmentBuildersModule.class)
    abstract MainActivity contributeMainActivity();
}

FragmentBuildersModule.java

@Module
public abstract class FragmentBuildersModule {
    @ContributesAndroidInjector
    abstract HomeFragment contributeRepoFragment();
}

AppComponent.java

@Singleton
@Component(modules = {
        AndroidInjectionModule.class,
        AppModule.class,
        MainActivityModule.class
})
public interface AppComponent {
    @Component.Builder
    interface Builder {
        @BindsInstance
        Builder application(Application application);

        AppComponent build();
    }
    void inject(FuelApp fuelApp);
}

AppInjector.java

public class AppInjector {
    private AppInjector() {}
    public static void init(FuelApp fuelApp) {
        DaggerAppComponent.builder().application(fuelApp)
                .build().inject(fuelApp);
        fuelApp.registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks() {
                    @Override
                    public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
                        handleActivity(activity);
                    }

                    @Override
                    public void onActivityStarted(Activity activity) {

                    }

                    @Override
                    public void onActivityResumed(Activity activity) {

                    }

                    @Override
                    public void onActivityPaused(Activity activity) {

                    }

                    @Override
                    public void onActivityStopped(Activity activity) {

                    }

                    @Override
                    public void onActivitySaveInstanceState(Activity activity, Bundle outState) {

                    }

                    @Override
                    public void onActivityDestroyed(Activity activity) {

                    }
                });
    }

    private static void handleActivity(Activity activity) {
        if (activity instanceof HasSupportFragmentInjector) {
            AndroidInjection.inject(activity);
        }
        if (activity instanceof FragmentActivity) {
            ((FragmentActivity) activity).getSupportFragmentManager()
                    .registerFragmentLifecycleCallbacks(
                            new FragmentManager.FragmentLifecycleCallbacks() {
                                @Override
                                public void onFragmentCreated(FragmentManager fm, Fragment f,
                                                              Bundle savedInstanceState) {
                                    if (f instanceof Injectable) {
                                        AndroidSupportInjection.inject(f);
                                    }
                                }
                            }, true);
        }
    }
}

ViewModelKey.java

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

TripViewModelFactory.java

@Singleton
public class TripViewModelFactory implements ViewModelProvider.Factory {
    private final Map<Class<? extends ViewModel>, Provider<ViewModel>> creators;

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

    @SuppressWarnings("unchecked")
    @Override
    public <T extends ViewModel> T create(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);
        }
    }
}

I have searched for a solution a lot but could not come with a working solution yet. Please feel free to share any comment. Thanks.

Edit: Below is how I Inject TripViewModel in my fragment class:

@Inject
ViewModelProvider.Factory viewModelFactory;
.
.
viewModel = ViewModelProviders.of(this, viewModelFactory).get(TripViewModel.class);
Fio
  • 3,088
  • 2
  • 13
  • 23
  • How / where do you provide `TripViewModel `? Neither did you show the (annotated) constructor nor a module that provides it. Please also see here: https://stackoverflow.com/q/44912080/1837367 – David Medenjak Feb 04 '18 at 19:59
  • @DavidMedenjak please see my edit – Fio Feb 04 '18 at 20:05
  • 1
    TripViewModel doesn't exist in your graph: You can inject TripViewModelFactory, but Dagger doesn't know how to get from one of those to the TripViewModel you're trying to bind in your graph. Furthermore, the code you've put for TripViewModelFactory seems to _consume_ the multibinding-created map, so binding any variety of TripViewModelFactory into that graph would make a circular reference. **What are you trying to do here?** When an object in your graph wants a TripViewModel, are you hoping that they inject TripViewModelFactory, or your Map? Or would they only want a TripViewModel _subclass_? – Jeff Bowman Feb 05 '18 at 18:27
  • Actually, I just want TripViewModel to be available as an object in the graph demands it and since my TripViewModel constructor has arguments I have to include a TripViewModelFactory. Your explanation makes sense however I still cannot come up with a solution. – Fio Feb 06 '18 at 15:55

1 Answers1

2

I have solved the problem. The error is caused due to wrong ViewModel constructor declaration. In my code I use a factory class (TripViewModelFactory) in order to use my TripViewModel class with a constructor having non-zero arguments which are injected by Dagger. My TripViewModel class constructor declaration was problematic and the constructor I intend to call by the factory class was not called. Thus a ViewModel instance is not created which caused the error given in my question in turn.

Also, if you use the same code structure that I share in the question, the fragment class which creates viewmodel instance from the factory class has to implement Injectable interface. This one has created another bug in my code, which will be nice to share.

Fio
  • 3,088
  • 2
  • 13
  • 23