Injecting ViewModels with an Activity context dependency

Injecting ViewModels with an Activity context dependency



I'm using dagger.android and want to inject ViewModels. On the other hand, I have an ApiModule that depends on activity context,


ApiModule



Here is the AppComponent


AppComponent


@Singleton
@Component(modules =
AndroidSupportInjectionModule.class,
AppModule.class,
ActivityBindingModule.class,
ViewModelModule.class
)
public interface AppComponent extends AndroidInjector<MyApp>
@Component.Builder
abstract class Builder extends AndroidInjector.Builder<MyApp>



The ViewModelModule provides ViewModelProvider.Factory


ViewModelModule


@Module
abstract class ViewModelModule
@Binds
abstract ViewModelProvider.Factory bindViewModelFactory(MyAppViewModelFactory factory);



Here is the ActivityBindingModule:


ActivityBindingModule


@Module
abstract class ActivityBindingModule
//MainActivity
@ActivityScoped
@ContributesAndroidInjector(modules =
MainActivityModule.class,
ApiModule.class
)
abstract MainActivity contributeMainActivityInjector();

@Module
abstract class MainActivityModule
@ActivityScoped
@Binds
abstract Activity bindMainActivity(MainActivity activity);

@Binds
@IntoMap
@ViewModelKey(UserViewModel.class)
abstract ViewModel bindUserViewModel(UserViewModel userViewModel);


//SecondActivity
@ActivityScoped
@ContributesAndroidInjector(modules =
SecondActivityModule.class,
ApiModule.class
)
abstract SecondActivity contributeSecondActivityInjector();

@Module
abstract class SecondActivityModule
@ActivityScoped
@Binds
abstract Activity bindSecondActivity(SecondActivity activity);

@Binds
@IntoMap
@ViewModelKey(DetailViewModel.class)
abstract ViewModel bindDetailViewModel(DetailViewModel detailViewModel);




I have these injection hierarchies:



MainActivity --inject--> ViewModelProvider.Factory --inject--> MyAppViewModelFactory --inject--> UserViewModel --inject--> UserRepository --inject--> ApiService --inject--> Activity context


MainActivity --inject--> ViewModelProvider.Factory --inject--> MyAppViewModelFactory --inject--> UserViewModel --inject--> UserRepository --inject--> ApiService --inject--> Activity context



SecondActivity --inject--> ViewModelProvider.Factory --inject--> MyAppViewModelFactory --inject--> DetailViewModel --inject--> DetailRepository --inject--> ApiService --inject--> Activity context


SecondActivity --inject--> ViewModelProvider.Factory --inject--> MyAppViewModelFactory --inject--> DetailViewModel --inject--> DetailRepository --inject--> ApiService --inject--> Activity context



We provide:


Activity


MainActivityModule


SecondActivityModule


ApiService


ApiModule


ActivityBindingModule


UserRepository


DetailRepository


@Singleton


UserViewModel


MainActivityModule


DetailViewModel


SecondActivityModule


MyAppViewModelFactory



Here is MyAppViewModelFactory:


MyAppViewModelFactory


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

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


@SuppressWarnings("unchecked")
@Override
@NonNull
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);





I got this error:


error: [Dagger/MissingBinding] [dagger.android.AndroidInjector.inject(T)] java.util.Map<java.lang.Class<? extends android.arch.lifecycle.ViewModel>,javax.inject.Provider<android.arch.lifecycle.ViewModel>> cannot be provided without an @Provides-annotated method.
java.util.Map<java.lang.Class<? extends android.arch.lifecycle.ViewModel>,javax.inject.Provider<android.arch.lifecycle.ViewModel>> is injected at
com.example.viewmodels.MyAppViewModelFactory.<init>(creators)
com.example.viewmodels.MyAppViewModelFactory is injected at
com.example.di.ViewModelModule.bindViewModelFactory(factory)
android.arch.lifecycle.ViewModelProvider.Factory is injected at
com.example.activities.MainActivity.viewModelFactory
com.example.activities.MainActivity is injected at
dagger.android.AndroidInjector.inject(T)
component path: com.example.di.AppComponent ? com.example.di.ActivityBindingModule_ContributeMainActivityInjector.MainActivitySubcomponent



I tried to move bindUserViewModel (from MainActivityModule) and bindDetailViewModel (from SecondActivityModule) to ViewModelModule and I got this error:


bindUserViewModel


MainActivityModule


bindDetailViewModel


SecondActivityModule


ViewModelModule


error: [Dagger/MissingBinding] [dagger.android.AndroidInjector.inject(T)] tk.medlynk.medlynk.http.ApiService cannot be provided without an @Provides-annotated method.
com.example.http.ApiService is injected at
com.example.repositories.UserRepository.<init>(apiService)
com.example.repositories.UserRepository is injected at
com.example.viewmodels.UserViewModel.<init>(userRepo)
com.example.viewmodels.UserViewModel is injected at
com.example.di.ViewModelModule.bindUserViewModel(userViewModel)
java.util.Map<java.lang.Class<? extends android.arch.lifecycle.ViewModel>,javax.inject.Provider<android.arch.lifecycle.ViewModel>> is injected at
com.example.viewmodels.MyAppViewModelFactory.<init>(creators)
com.example.viewmodels.MyAppViewModelFactory is injected at
com.example.di.ViewModelModule.bindViewModelFactory(factory)
android.arch.lifecycle.ViewModelProvider.Factory is injected at
com.example.activities.MainActivity.viewModelFactory
com.example.activities.MainActivity is injected at
dagger.android.AndroidInjector.inject(T)
component path: com.example.di.AppComponent ? com.example.di.ActivityBindingModule_ContributeMainActivityInjector.MainActivitySubcomponent



What am I missing here? the iosched project does the same thing without problem




2 Answers
2



Firstly, you need to restructure your Dagger2 setup in the Application and Activity levels.
Secondly, your ViewModelProviderFactory class is somewhat complicated. I will restructure your code for you now.


ViewModelProviderFactory



Application Level



AppComponent


AppComponent


@Singleton
@Component(modules =
AndroidSupportInjectionModule.class,
AppModule.class,
ActivityBindingModule.class
)
public interface AppComponent extends AndroidInjector<MyApp>
@Component.Builder
abstract class Builder extends AndroidInjector.Builder<MyApp>



ActivityBindingModule


ActivityBindingModule


@Module
abstract class ActivityBindingModule

@ContributesAndroidInjector(modules = MainActivityModule.class)
abstract MainActivity contributeMainActivity();

//This goes for other activities



AppModule This class provides only classes that are common and may be used in
any activity throughout the application.


AppModule


@Module
class AppModule

@Singleton
@Provides
//Your ApiService interface comes here
//Retrofit
//Database



App class should extend DaggerApplication for simplicity. This automatically injects its members


App


class MyApp extends DaggerApplication

@Override
public void onCreate()
super.onCreate();


@Override
protected AndroidInjector<? extends DaggerApplication>
applicationInjector()
return DaggerHorizonMainComponent.builder().create(this);



Activity Level



MainActivityModule This is where you have your ViewModel and ViewModelProviderFactory


MainActivityModule


ViewModel


ViewModelProviderFactory


@Module
class MainACtivityModule

@Provides
MainActivityViewModel provideMainActivityViewModel()
return new MainActivityViewModel();


@Provides
ViewModelProvider.Factory provideViewModelProvider(MainActivityViewModel
viewModel)
return new ViewModelProviderFactory<>(viewModel);


//Your repository comes here too



ViewModel class


ViewModel


class MyViewModel extends ViewModel

@Inject
public MyViewModel()




ViewModelProviderFactory Just copy this code


ViewModelProviderFactory


public class ViewModelProviderFactory<V> implements
ViewModelProvider.Factory
private V viewModel;

public ViewModelProviderFactory(V viewModel)
this.viewModel = viewModel;


@Override
public <T extends ViewModel> T create(Class<T> modelClass)
if (modelClass.isAssignableFrom(viewModel.getClass()))
return (T) viewModel;

throw new IllegalArgumentException("Unknown class name");




Finally, in your MainActivity class, you inject the ViewModelProviderFactory. You don't need to inject Dagger here since you are extending it to DaggerAppCompatActivity


MainActivity


public class Mainactivity extends DaggerAppCompatActivity

@Inject
ViewModelProvider.Factory factory;
private MyViewModel viewModel;

@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
//You set your view here
viewmodel =
ViewModelProviders.of(this,factory).get(MyViewModel.class);







Tnx, but I used ViewModelProviderFactory as same as google sample github.com/googlesamples/android-architecture-components/blob/… and same as github.com/google/iosched/blob/master/shared/src/main/java/com/…
– Hafez Divandari
Aug 26 at 22:47



ViewModelProviderFactory





Let me know if there is any issue regarding the above codes or if you have any question. Hope this solves the issues you have
– princessdharmy
Aug 26 at 23:16



All your setting sounds correct. The main cause is ApiService is not provided.


ApiService



error: [Dagger/MissingBinding]
[dagger.android.AndroidInjector.inject(T)]
tk.medlynk.medlynk.http.ApiService cannot be provided without an
@Provides-annotated method.



To resolve it you just provide it in some module.




class AppModule
@Singleton
@Provides
fun provideApiService(): ApiService
....







By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Popular posts from this blog

𛂒𛀶,𛀽𛀑𛂀𛃧𛂓𛀙𛃆𛃑𛃷𛂟𛁡𛀢𛀟𛁤𛂽𛁕𛁪𛂟𛂯,𛁞𛂧𛀴𛁄𛁠𛁼𛂿𛀤 𛂘,𛁺𛂾𛃭𛃭𛃵𛀺,𛂣𛃍𛂖𛃶 𛀸𛃀𛂖𛁶𛁏𛁚 𛂢𛂞 𛁰𛂆𛀔,𛁸𛀽𛁓𛃋𛂇𛃧𛀧𛃣𛂐𛃇,𛂂𛃻𛃲𛁬𛃞𛀧𛃃𛀅 𛂭𛁠𛁡𛃇𛀷𛃓𛁥,𛁙𛁘𛁞𛃸𛁸𛃣𛁜,𛂛,𛃿,𛁯𛂘𛂌𛃛𛁱𛃌𛂈𛂇 𛁊𛃲,𛀕𛃴𛀜 𛀶𛂆𛀶𛃟𛂉𛀣,𛂐𛁞𛁾 𛁷𛂑𛁳𛂯𛀬𛃅,𛃶𛁼

Edmonton

Crossroads (UK TV series)