Dagger 2 dependency cycle - dagger-2

I've next problem with injecting ViewModel into fragment:
error: [Dagger/DependencyCycle] Found a dependency cycle:
public interface FragmentComponent {
^
...ViewModelFactory is injected at
...ViewModelModule.bindViewModelFactory(viewModelFactory)
...ViewModelFactory is injected at
...SplashFragment.viewModelFactory
...SplashFragment is injected at
...FragmentComponent.inject(ru.syncended.jlocationtracker.auth.fragments.SplashFragment)
Where FragmentComponent code:
public interface FragmentComponent {
void inject(SplashFragment splashFragment);
}
ViewModelFactory code:
private final Map<Class<? extends ViewModel>, Provider<ViewModel>> viewModelProviders;
#Inject
public ViewModelFactory(Map<Class<? extends ViewModel>, Provider<ViewModel>> viewModelProviders) {
this.viewModelProviders = viewModelProviders;
}
#NonNull
#Override
public <T extends ViewModel> T create(#NonNull Class<T> modelClass) {
Provider<ViewModel> viewModelProvider = viewModelProviders.get(modelClass);
return (T) viewModelProvider.get();
}
ViewModelModule code:
#Binds
ViewModelFactory bindViewModelFactory(ViewModelFactory viewModelFactory);
#Binds
#IntoMap
#ViewModelKey(SplashViewModel.class)
SplashViewModel bindSplashViewModel(SplashViewModel splashViewModel);
And SplashViewModel code:
#Inject
public SplashViewModel(IAuthRepository repository) ...
How I can to solve that? It's isn't like that it has dependency cycle

#Binds
ViewModelFactory bindViewModelFactory(ViewModelFactory viewModelFactory);
This is a dependency "cycle" of size 1. It tells Dagger that, in order to get an instance of ViewModelFactory, you must first get an instance of ViewModelFactory and use that. This replaces the #Inject constructor that would otherwise be used.
To fix this, simply remove this #Binds method. Dagger already knows how to create a ViewModelFactory from its #Inject constructor. Alternatively, you can depend on the ViewModelProvider.Factory interface instead, and change the #Binds method's return type accordingly.

I've solved that problem by next solution:
I changed ViewModelModule code from:
#Binds
#IntoMap
#ViewModelKey(SplashViewModel.class)
SplashViewModel bindSplashViewModel(SplashViewModel splashViewModel);
To:
#Binds
#IntoMap
#ViewModelKey(SplashViewModel.class)
ViewModel bindSplashViewModel(SplashViewModel splashViewModel);

Related

Infinite recursion when using subcomponent for encapsulation

I'm trying to achieve encapsulation by using subcomponent which is described here, but I got infinite recursion.
Here is my code:
//tried adding #ScopeA, still the same.
public class A {
#Inject
A(B b) {
}
}
#ScopeA
public class B {
#Inject
B() {
}
}
#Component(modules = AModule.class)
#Singleton
public interface AComponent {
public A a();
}
#Module(subcomponents = SComponent.class)
class AModule {
#Provides
#Singleton
A a(SComponent.Factory factory) {
return factory.component().a();
}
}
#Subcomponent
#ScopeA
interface SComponent {
#ScopeA
A a();
#Subcomponent.Factory
interface Factory {
SComponent component();
}
}
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerAComponent.create().a();
}
}
After checking generated dagger code, I found this:
private final class SComponentImpl implements SComponent {
private SComponentImpl() {}
#Override
public A a() {
return DaggerAComponent.this.aProvider.get();
}
}
It seeems that SComponent are getting A from parent component, which is not what I wanted, where is the problem of my code?
Note that the example from the Subcomponents for Encapsulation page uses a qualifier annotation, #PrivateToDatabase, which is not a scoping annotation and which distinguishes the binding of Database from the binding of #PrivateToDatabase Database.
Subcomponents inherit all of the bindings from their parent components, so you currently do have A available from the parent component and also A available from the subcomponent. This is especially tricky if anything in your subcomponent needs to inject A, if it weren't marked #Singleton: Do you want the A from the parent component, or the A from the subcomponent?
Another tricky part of this situation is that you can't use qualifier annotations on classes that use #Inject constructors.
I'd recommend that you do the following:
Extract an interface from A, so then you have A and AImpl.
Keep your #Provides method that gets an A instance from the subcomponent.
Have the subcomponent expose AImpl, and (to best avoid ambiguity) only inject AImpl in the classes in your subcomponent, not A.
If you'd rather not extract an interface, you could also work around this problem by removing #Inject from A and writing a #Provides method in a module in the subcomponent that returns a qualified A, so the unqualified A goes through the top-level component and the qualified A is only available within the subcomponent.

Constructor injecting make MissingBinding in dagger 2 and mvp

I have created simple project with dagger2 and mvp.
This is my component :
#MainScope
#Component(modules = {MainModule.class})
public interface IMainComponent {
void inject(MainActivity mainActivity);
}
and This is MainModule.class:
#Module
public class MainModule {
#MainScope
#Provides
IMain.IMainModel model() {
return new MainModel();
}
}
Now in presenter i want to inject presenter from it's constructor so i do :
public class MainPresenter implements IMain.IMainPresenter {
IMain.IMainModel model;
IMain.IMainView view;
#Inject
public MainPresenter(IMain.IMainModel model) {
this.model = model;
}
But i got This error:
symbol: class DaggerIMainComponent
location: package com.safarayaneh.engineer.main.di
E:\Projects\Android\NewEng\Engineer\engineer\src\main\java\com\safarayaneh\engineer\main\di\IMainComponent.java:9: error: [Dagger/MissingBinding] com.safarayaneh.engineer.main.mvp.IMain.IMainPresenter cannot be provided without an #Provides-annotated method.
When make provider in MainModule.class to create presenter and remove #Inject above presenter constructor , everything is fine:
#Module
public class MainModule {
#MainScope
#Provides
IMain.IMainModel model() {
return new MainModel();
}
#MainScope
#Provides
IMain.IMainPresenter presenter(IMain.IMainModel model) {
return new MainPresenter(model);
}
}
Your problem is that your Activity expects IMain.IMainPresenter, but if you just annotate the constructor then what's placed on the objects graph is the concrete MainPresenter.
You've got three options here:
Use explicit provider method (as you did)
Use #Binds annotation inside the module to specify that MainPresenter should be provided as IMain.IMainPresenter
Don't use interface for the presenter

Dagger2: wildcard with Generics

I'm new to Dagger2 and DI in general, but I'm interested to populate a map with injected keys/values. The problem is that it works if I provide the exact types, I can't make it work with wildcards, any solution for that?
#Module
public class SimpleIssueModule
{
#Provides
#Singleton
#IntoMap
#StringKey("simple_issue")
public SimpleIssue provideSimpleIssue()
{
return new SimpleIssue();
}
}
#Module
public class DaggerFactoryModule
{
#Provides
#Singleton
public Factory provideFactory(Map<String, Provider< ? extends Issue>> map)
{
return new Factory(map);
}
}
If you want a map of Provider< ? extends Issue>> map, then you need to use Issue as the type returned in your module. Dagger will not do any casting or guessing on its own.
#Provides
#Singleton
#IntoMap
#StringKey("simple_issue")
public Issue provideSimpleIssue() {
return new SimpleIssue();
}
what to do in case I need a Module that provides a base class (Issue) into a Map and also need a provider of the concrete class (SimpleIssue) and I would like it to be Singleton (same instance returns in both cases)
In this case you provide the #Singleton of SimpleIssue.
#Provides
#Singleton
public SimpleIssue provideSimpleIssue() {
return new SimpleIssue();
}
// or you can use constructor injection, dropping the method above...
#Singleton
public class SimpleIssue {
#Inject
public SimpleIssue(...) {
}
}
Then you bind this instance into a Map. There is no need for a scope, since the implementation should declare it (as done above).
#Provides
#IntoMap
#StringKey("simple_issue")
public Issue provideSimpleIssue(SimpleIssue issue) {
return issue;
}
// or alternatively with `#Binds` when using an abstract class / interface
// this leads to actually better performing dagger code
#Binds
#IntoMap
#StringKey("simple_issue")
public Issue provideSimpleIssue(SimpleIssue issue);

About Dagger 2. Connection of #inject and #provide

If there is #inject, then it means there must be #provide?
inject field gets its value from #provide method of module?
Yes if you use Module
#Module
public class SomeModule {
#Provides
Unscoped unscoped() {
return new Unscoped();
}
#Provides
#Singleton
Scoped scoped() {
return Scoped();
}
}
BUT classes with #Inject constructor get automatically appended to your scoped component even if no module is specified for it:
#Singleton
public class Scoped {
#Inject
public Scoped() {
}
}
public class Unscoped {
#Inject
public Unscoped() {
}
}
If there is #Inject annotation then it's dependency can be provided in two ways :
By Using Provides annotation in module
#Provides
TasksPresenter provide TasksPresenter(TasksRepository tasksRepository, TasksContract.View tasksView) {
return new TasksPresenter(tasksRepository,tasksView);
}
By Using Constructor Injection
#Inject
TasksPresenter(TasksRepository tasksRepository, TasksContract.View tasksView) {
mTasksRepository = tasksRepository;
mTasksView = tasksView;
}
One thing to observe here is Constructor Injection solve two thing
Instantiate object
Provides the object by adding it to Object graph.

Tapestry IoC constructor and injection

I have the following class:
public class MyClass {
#Inject
private MyAnotherClass myAnotherClass;
public MyClass() {
//Perform operations on myAnotherClass.
}
}
I need to do some things in constructor which require an instance of myAnotherClass. Unfortunately myAnotherClass is injected after code in constructor is ran, which means I am performing operations on null...
I could of course instantiate it the classic way (MyAnotherClass myAnotherClass = new MyAnotherClass()) directly in constructor, but I don't think it is the right thing to do in this situation.
What solutions would you suggest to solve this problem?
Best option:
public class MyClass {
private final MyAnotherClass myAnotherClass;
public MyClass(MyAnotherClass other) {
this.myAnotherClass = other;
// And so forth
}
}
T5-IoC will then use constructor injection so there's no need to 'new' up MyClass yourself. See Defining Tapestry IOC Services for more info.
Alternatively:
public class MyClass {
#Inject
private MyAnotherClass myAnotherClass;
#PostInjection
public void setupUsingOther() {
// Called last, after fields are injected
}
}