Method injection using Dagger 2

One fundamental difference about Dagger method injection, compared to the way you are using it, is that Dagger method injection is just another way for Dagger to send in dependencies when constructing or injecting a DI-ready object, which means that @Inject-annotated methods are meant to be called by Dagger once on construction and not from within your own code. This makes it very very unlikely that you would @Inject-annotate makeDinner, fryDinner, or any other method that has meaningful side effects or return values. Instead, treat method injection as a post-construction opportunity for constructor-style injection.

(Of course, you can always practice general dependency injection at the method level, passing your dependencies into a method call so that the method itself does not have to create them. However, this is not what Dagger means with its definition of method injection, and it does not help support that case.)

public class Chef {
  private Provider<Pasta> mPastaProvider;
  private Sauce mSauce;

  @Inject
  public void registerIngredients(     // can be named anything
      Provider<Pasta> pastaProvider,
      Sauce sauce) {                   // T and Provider<T> both work, of course
    mPastaProvider = pastaProvider;
    mSauce = sauce;
  }

  /* Non-@Inject */ public Dinner cookDinner() {
    mPan.add(mPastaProvider.get());
    mPan.add(mSauce);
    return mPan.cookDinner();
  }

  /* Non-@Inject */ public Dinner fryDinner() {
    mPan.add(mPastaProvider.get());
    mPan.add(mSauce);
    return mPan.fryDinner();
  }
}

In this case, when you request injection on a Chef instance, Dagger will see the @Inject-annotated method and call it with arguments from the Dagger graph.

This works with or without Chef being Dagger-constructable: Unless you have an @Inject-annotated constructor or @Provides method, you won’t be able to get a Chef directly from your Component, but you could create a void method on the Component which receives an already-constructed Chef instance. That Component method would use field and method injection to provide that Chef with the ingredients, Providers, Optionals, or Lazys they may need. See the @Component and MembersInjector docs for details.

Note that in no case does Dinner appear available on the object graph! Adding @Inject to a method or field merely tells Dagger that as part of the injection process it should populate that field or call that method with the given dependencies. If you want to make a Dinner available on the object graph, you’ll need to @Inject-annotate the Dinner constructor, or put a @Provides or @Binds method on a Module that you feed into the Component.

Why would you use method injection? Though constructor injection is preferable, and allows the class’s fields to be final, consider a case where objects are created reflectively (e.g. Activities, Fragments, and Views in Android, or Serializable objects). Field injection (where Dagger populates an @Inject-annotated field) would work as well, but in some cases you may prefer not to expose @Inject-annotated fields. In those cases, you could work around constructor constraints by having your injection happen on an @Inject-annotated method. Similarly, though I haven’t tried this, you could take advantage of class hierarchy to mark an interface method with @Inject: this would ensure that whether or not you’re in a DI context you can pass certain dependencies to an object as part of their preparation.

Leave a Comment