Je suis nouveau sur Dagger 2. J'ai 2 activités, je veux utiliser ViewModel injecté pour les deux. Voici mon ViewModuleFactory:
@Singleton
public class ProductViewModelFactory implements ViewModelProvider.Factory {
private final Map<Class<? extends ViewModel>, Provider<ViewModel>> creators;
@Inject
public ProductViewModelFactory(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 viewmodel class " + modelClass);
}
try {
return (T) creator.get();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
Mon ViewModelModule:
@Module
abstract class ViewModelModule {
@Binds
@IntoMap
@ViewModelKey(ProductListViewModel.class)
abstract ViewModel bindProductListViewModel(ProductListViewModel listViewModel);
@Binds
@IntoMap
@ViewModelKey(ProductDetailsViewModel.class)
abstract ViewModel bindProductDetailsViewModel(ProductDetailsViewModel detailsViewModel);
@Binds
abstract ViewModelProvider.Factory bindViewModelFactory(ProductViewModelFactory factory);
}
Mon ViewModelKey pour le mappage:
@Documented
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@MapKey
@interface ViewModelKey {
Class<? extends ViewModel> value();
}
Mon ActivityModule:
@Module
public abstract class ActivityModule {
abstract ProductListActivity contributeProductListActivity();
abstract ProductDetailsActivity contributeProductDetailsActivity();
}
Mon AppModule:
@Module
class AppModule {
@Provides
@Singleton
RedMartProductService provideRedMartProductService() {
........
}
@Provides
@Singleton
ProductListRepository provideProductListRepository(ProductListRepository repository) {
return repository;
}
@Provides
@Singleton
ProductDetailsRepository provideProductDetailsRepository(ProductDetailsRepository repository) {
return repository;
}
}
Mon AppComponent:
@Singleton
@Component(modules = {AndroidInjectionModule.class, ActivityModule.class, AppModule.class})
public interface AppComponent {
@Component.Builder
interface Builder {
@BindsInstance
Builder application(Application application);
AppComponent build();
}
void inject(MartApplication martApp);
}
Mon Application:
public class MartApplication extends Application implements HasActivityInjector {
@Inject
DispatchingAndroidInjector<Activity> dispatchingAndroidInjector;
@Override
public void onCreate() {
super.onCreate();
}
@Override
public DispatchingAndroidInjector<Activity> activityInjector() {
return dispatchingAndroidInjector;
}
}
Dans Activité:
@Inject
ViewModelProvider.Factory viewModelFactory;
.......
AndroidInjection.inject(activity); // Throwing exception
ListViewModel = ViewModelProviders.of(this, viewModelFactory).get(ProductListViewModel.class);
Il lance une exception sur injecter:
Java.lang.IllegalArgumentException: No injector factory bound for Class<com.mymart.ui.ProductListActivity>
Quelqu'un peut-il m'aider à identifier le problème dans mon code?
.................................................. .....................
Edit: J'ai essayé avec ContributesAndroidInjector
selon @azizbekian, mais il en est résulté une erreur suivante sur la construction:
error: [dagger.Android.AndroidInjector.inject(T)] Found a dependency cycle:
com.mymart.repository.ProductListRepository is injected at
com.mymart.di.AppModule.provideProductListRepository(repository)
com.mymart.repository.ProductListRepository is injected at
com.mymart.viewmodel.ProductListViewModel.<init>(productListRepository)
com.mymart.viewmodel.ProductListViewModel is injected at
com.mymart.di.ViewModelModule.bindProductListViewModel(listViewModel)
Java.util.Map<Java.lang.Class<? extends Android.Arch.lifecycle.ViewModel>,javax.inject.Provider<Android.Arch.lifecycle.ViewModel>> is injected at
com.mymart.viewmodel.ProductViewModelFactory.<init>(creators)
com.mymart.viewmodel.ProductViewModelFactory is injected at
com.mymart.di.ViewModelModule.bindViewModelFactory(factory)
Android.Arch.lifecycle.ViewModelProvider.Factory is injected at
com.mymart.ui.ProductListActivity.viewModelFactory
com.mymart.ui.ProductListActivity is injected at
dagger.Android.AndroidInjector.inject(arg0)
Edit 2 Après tous les changements, je fais face à une exception:
Java.lang.RuntimeException: Unable to create application com.kaushik.myredmart.MartApplication: Java.lang.IllegalStateException: com.kaushik.myredmart.di.AppModule must be set
at Android.app.ActivityThread.handleBindApplication(ActivityThread.Java:4710)
at Android.app.ActivityThread.-wrap1(ActivityThread.Java)
at Android.app.ActivityThread$H.handleMessage(ActivityThread.Java:1405)
at Android.os.Handler.dispatchMessage(Handler.Java:102)
at Android.os.Looper.loop(Looper.Java:148)
at Android.app.ActivityThread.main(ActivityThread.Java:5417)
at Java.lang.reflect.Method.invoke(Native Method)
at com.Android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.Java:726)
at com.Android.internal.os.ZygoteInit.main(ZygoteInit.Java:616)
Caused by: Java.lang.IllegalStateException: com.kaushik.myredmart.di.AppModule must be set
at com.kaushik.myredmart.di.DaggerAppComponent$Builder.build(DaggerAppComponent.Java:180)
at com.kaushik.myredmart.di.AppInjector.init(AppInjector.Java:30)
at com.kaushik.myredmart.MartApplication.onCreate(MartApplication.Java:28)
at Android.app.Instrumentation.callApplicationOnCreate(Instrumentation.Java:1013)
at Android.app.ActivityThread.handleBindApplication(ActivityThread.Java:4707)
at Android.app.ActivityThread.-wrap1(ActivityThread.Java)
at Android.app.ActivityThread$H.handleMessage(ActivityThread.Java:1405)
at Android.os.Handler.dispatchMessage(Handler.Java:102)
at Android.os.Looper.loop(Looper.Java:148)
at Android.app.ActivityThread.main(ActivityThread.Java:5417)
at Java.lang.reflect.Method.invoke(Native Method)
Je crois que tu as oublié de mettre @ContributesAndroidInjector
annotation:
@Module
public abstract class ActivityModule {
@ContributesAndroidInjector
abstract ProductListActivity contributeProductListActivity();
@ContributesAndroidInjector
abstract ProductDetailsActivity contributeProductDetailsActivity();
}
Et incluez ViewModelModule
dans AppModule
:
@Module(includes = ViewModelModule.class)
class AppModule {
...
}
Voir ce code que vous avez écrit:
@Provides
@Singleton
ProductListRepository provideProductListRepository(ProductListRepository repository) {
return repository;
}
Qu'espérez-vous qu'il se passe? Vous dites à la dague "hé, dague, chaque fois que je vous demande de me fournir ProductListRepository
puis créez (retournez) cet objet en utilisant ProductListRepository
". Ça ne va pas marcher.
Probablement ce que vous vouliez était "hé, dague, chaque fois que je vous demande de me fournir une implémentation de ProductListRepository
puis créez (retournez) cet objet en utilisant ProductListRepositoryImpl
":
@Provides
@Singleton
ProductListRepository provideProductListRepository(ProductListRepositoryImpl repository) {
return repository;
}
Ce qui peut être remplacé par ce qui suit:
@Binds
@Singleton
abstract ProductListRepository provideProductListRepository(ProductListRepositoryImpl repository);