Dagger2 not resolving dependency all the way

前端 未结 2 903
南笙
南笙 2021-01-26 02:56

I have a MainActivity which injects Presenter, presenter object injects interactor and interactor object injects APIHelper. All the providers of presenter, interactor and APIHel

相关标签:
2条回答
  • 2021-01-26 03:29

    Change MainPresenterImpl & ListingInteractorImpl constructors to following and precede them with @Inject:

    
        @Inject
        public MainPresenterImpl(MainActivity activity, ListingInteractor interactor) {...}
    
        @Inject
        public ListingInteractorImpl(Context context, APIHelper helper) {...}
    
    

    Then in your module implementation:

    
        @Provides
        @Singleton
        public MainViewPresenter providesMainPresenter(ListingInteractor interactor){
            return new MainPresenterImpl(activity, interactor);
        }
    
        @Provides
        @Singleton
        ListingInteractor providesInteractor(APIHelper helper){
            return new ListingInteractorImpl(activity, helper);
        }
    
    
    
    0 讨论(0)
  • 2021-01-26 03:31

    Dagger is not magic. It will not magically insert objects wherever you want unless you tell it to do so.

    public class MainPresenterImpl implements MainViewPresenter {
      // ... other fields ...
      @Inject
      ListingInteractor interactor;
    
      public MainPresenterImpl(MainActivity activity) {
        this.context = activity;
        this.mainView = activity;
      }
    }
    

    To Dagger this is...nothing. You marked some field (ListingInteractor) for field injection, but unless you manually call a component to inject your object nothing will happen. Field injection should be reserved for Activities and Fragments where you can't add arguments to the constructor, not for your average classes.

    @Provides
    @Singleton
    MainViewPresenter providesMainPresenter(){
      return new MainPresenterImpl(activity);
    }
    

    Instead of letting Dagger create MainPresenterImpl for you, you make a call to new MainPresenterImpl() yourself, only passing in the Activity. Since there is no call to MainPresenterImpl.interactor, it will be null. You're not using field injection, you're calling the constructor yourself and you're not assigning the field.
    Manually creating objects in modules should be reserved for objects that require further setup, like Retrofit or OkHttp with their builders.

    If you want your fields to be set, you could use field injection and register your objects with the Component (those inject(FieldInjectableClass clazz) methods) and sprinkle component.inject(myObject) throughout your code, which would be a really bad idea because you'd end up writing a lot of boilerplate that you don't need.

    The more reasonable way is to move your dependencies to the constructor, where they belong.

    public MainPresenterImpl(MainActivity activity, ListingInteractor interactor) { /* ... */ }
    

    If you have a dependency on another class, why not declare it as such? But this still leaves the boilerplate of you creating the object yourself, instead of letting Dagger do its job.

    That's why you should use Constructor Injection. As the name suggests, it's about the constructor. Just add the @Inject annotation:

    @Inject // marked for constructor injection!
    public MainPresenterImpl(MainActivity activity, ListingInteractor interactor) { /* ... */ }
    

    Now Dagger knows about this entry point to your class and can create it.

    To let dagger handle things you could add the @Singleton scope to the class itself (annotate the class, not the constructor) and just delete the @Provides method for it (there is no need for provides methods in modules for objects that don't need further setup), but since you're binding an Implementation to an Interface you still need to specify which class you want to bind to the interface.

    @Provides
    @Singleton
    MainViewPresenter providesMainPresenter(MainPresenterImpl implementation){
      return implementation;
    }
    

    Since Dagger can create MainPresenterImpl with constructor injection you can return the implementation for your interface, and there is no need to update any code in case the constructor signature changes, as Dagger will just adapt the class instantiation accordingly.

    That's how to use constructor injection and bind implemenations to interfaces. And as mentioned, I recommend highly to read up on the basics. Make sure you understand what Dagger does and how it works. Be sure to know the difference between field and constructor injection, or when to use modules.

    The time you invest now in learning Dagger will mean much less debugging and errors later on.


    In case that your module is abstract or an interface, you can also make use of the @Binds method, where Dagger will just generate the boilerplate code above.

    @Binds
    @Singleton
    MainViewPresenter providesMainPresenter(MainPresenterImpl implementation);
    
    0 讨论(0)
提交回复
热议问题