I'm refactoring an Android app to an MVP architecture, using Dagger 1.
My code is more or less the following:
public interface View {
Presenter getPresenter();
}
public abstract class Presenter {
// Dagger 1 can't have an abstract class without injectable members... :(
@Inject
Application dont_need_this;
protected View view;
public void takeView(View view) { ... }
}
The field in Presenter
is not pretty, but what can we do?
Next, I have an Activity
that's also a View
.
public abstract class ActivityView extends Activity implements View {
@Inject
protected Presenter presenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ObjectGraph scope;
// Looks for an "scoped" ObjectGraph hold in an retained fragment
if (/* fragment is null */) {
// This is not a rotation but the first time the activity is
// launched
MVPApplication app = (MVPApplication) getApplication();
ObjectGraph scope = app.createScope(this);
// Store in the fragment
}
scope.inject(this);
// setContentView, etc.
presenter.takeView(this);
}
}
All seems good for now...
public abstract class MVPApplication extends Application {
private ObjectGraph objectGraph;
@Override
public void onCreate() {
super.onCreate();
// Root scope
// createRootModules is defined in the subclass
objectGraph = ObjectGraph.create(createRootModules());
objectGraph.inject(this);
}
public abstract ObjectGraph createScope(View view);
}
With this, my application subclass has to:
- define
createRootModules()
to instantiate every module in the root scope - check in
createScope()
the class of the concrete ActivityView, essentially using aHashMap<Class<? extends View>, Module>
and do...
return objectGraph.plus(new ModuleForThisActivityViewClass());
For example, I have an CollectionActivityView
. My app tries to...
return objectGraph.plus(new CollectionModule());
And I have the binding for this particular Presenter
in this module:
@Module(addsTo = MyAppModule.class,
injects = {CollectionActivityView.class, CollectionPresenter.class}, complete=false)
public class CollectionModule {
@Provides
@Singleton
public Presenter providePresenter(CollectionPresenter presenter) {
return presenter;
}
}
But when doing the 'plus()` this error happens:
ComponentInfo{com.mycompany.myapp/com.mycompany.myapp.view.CollectionActivityView}:
java.lang.IllegalStateException: Unable to create binding for
com.mycompany.myapp.mvp.presenter.Presenter
...
Caused by: java.lang.IllegalStateException: Unable to create binding for
com.mycompany.myapp.mvp.presenter.Presenter
at dagger.internal.Linker.linkRequested(Linker.java:147)
at dagger.internal.Linker.linkAll(Linker.java:109)
at dagger.ObjectGraph$DaggerObjectGraph.linkEverything(ObjectGraph.java:244)
at dagger.ObjectGraph$DaggerObjectGraph.plus(ObjectGraph.java:203)
Dagger is trying to resolve the dangling dependency of the superclass Presenter
in ActivityView
before plusing the ObjectGraph with the new Module, who is in charge of providing the Presenter.
Is there a way to avoid this? Am I doing something terribly wrong? Do you have any suggestions for doing this properly?
Looking at it again, it was my fault.
I don't know why I did it (probably trying to avoid tagging the module as incomplete or library) but
MyAppModule
declared it was injectingCollectionActivityView
, so thePresenter
dependency had to be resolved in that module before plussing the other. Removed that from the annotation inMyAppModule
and everything worked as intended.