Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

I haven't managed to find a good explanation/example on method injection using Dagger 2. Could someone please help me understand?

Example:

@Inject
public Dinner makeDinner(Pasta pasta, Sauce sauce) {
    mPan.add(pasta);
    mPan.add(sauce);
    return mPan.cookDinner();
}

So if I annotate my method with @Inject, am I correct to assume that the arguments in the method signature will be injected with defined objects from the object graph? How can I use this method in my code then? It will still expect me to supply all the arguments, when I make the method call, which sort of defeats the purpose.

UPDATE:

So from what I understand the Dinner object will be available if I call DinnerComponent.dinner(), assuming my DinnerComponent is set up like this:

@Component(modules = DinnerModule.class)
public interface DinnerComponent {
    Dinner dinner();
}

and my DinnerModule is set up like this:

@Module
public class DinnerModule {
    public DinnerModule() {}

    @Provides
    Pasta providePasta() { return new Pasta(); }

    @Provides
    Sauce provideSauce() { return new Sauce(); }
}

What happens if I want my dinner fried? So let's introduce this method:

@Inject
public Dinner makeDinner(Pasta pasta, Sauce sauce) {
    mPan.add(pasta);
    mPan.add(sauce);
    return mPan.fryDinner();
}

How can I specify within the component which dinner is which?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
145 views
Welcome To Ask or Share your Answers For Others

1 Answer

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
...