Dagger can't inject a Presenter in my Activities

1k Views Asked by At

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 a HashMap<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?

1

There are 1 best solutions below

0
On BEST ANSWER

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 injecting CollectionActivityView, so the Presenter dependency had to be resolved in that module before plussing the other. Removed that from the annotation in MyAppModule and everything worked as intended.