I am new to Dagger (version 2.16 on Android) and based on my reading so far, I understand that for a component, there should be a provider (@Provides
or @Binds
) encapsulated in a module (@Module
). Going through a lot of samples, I see code which has some objects which are not offered in any Module, nor are they being instantiated using new.
It is also my understanding that in order to access the dependencies in a module, a consumer class needs to inject itself in the component graph (components usually offer a method to inject classes). The code examples are not doing this either.
Here's some code demonstrating both my concerns. RecipePresenter is not being provided in any module, but still RecipeActivity is using it.
A possible explanation I could think of is that @Inject
, in addition to requesting the dependency also adds/injects the requesting class (RecipePresenter
in the linked code) into the component graph. But assuming there are multiple components/subcomponents, which component does the class using @Inject
constructor gets attached to? If my understanding is correct, why do activities and fragments have to inject themselves manually in the component - shouldn't they be auto-injected if they declare a variable annotated with @Inject
?
RecipePresenter has an @Inject
-annotated constructor, which allows it to be provided. The @Inject
annotation on the recipePresenter
field within RecipeActivity does not help, just the @Inject
-annotated constructor.
class RecipePresenter @Inject constructor(
private val useCase: RecipeUseCase,
private val res: StringRetriever) :
RecipeUseCase.Callback {
Use
@Inject
to annotate the constructor that Dagger should use to create instances of a class. When a new instance is requested, Dagger will obtain the required parameters values and invoke this constructor.
If the class with the @Inject
-annotated constructor also has a defined scope, then the binding will only affect components with the same scope annotation: An @ActivityScope
class wouldn't be accessible from a @Singleton
component, for instance. If the class has no scope defined, then the binding could appear on any and all components where it is needed, which is fine: The implementation is always the same, defined by the constructor itself.
@Inject
has different meanings based on what it's annotating:
@Inject
, it indicates that the DI system should set that field based on values from the DI system when it injects that object.@Inject
, it indicates that the DI system should call that method with parameters based on values from the DI system when it injects that object.@Inject
, it indicates that the DI system is allowed to call that constructor in order to create that object (which triggers the field and method injection above). In that sense it does act like a built-in Provider.In Dagger specifically, @Provides
methods take precedence over @Inject
constructors (if they both exist for the same class), and unlike in the JSR-330 spec Dagger requires an @Inject
-annotated constructor even when no other constructors are present. From the Dagger User's Guide:
If your class has
@Inject
-annotated fields but no@Inject
-annotated constructor, Dagger will inject those fields if requested, but will not create new instances. Add a no-argument constructor with the@Inject
annotation to indicate that Dagger may create instances as well.Classes that lack
@Inject
annotations cannot be constructed by Dagger.
Finally, to answer your question about why activities and fragments need to inject themselves: Android is allowed to reflectively create Activity/Fragment/View objects without Dagger's help or participation. This means that nothing triggers the field and method injection described above, until you call a members-injection method on the component that instructs Dagger to populate those fields and call those methods. Consequently, you should never see an @Inject
-annotated constructor on Application, Activity, Service, Fragment, View, ContentProvider, and BroadcastReceiver subclasses in Android: Android will ignore the @Inject
annotation, so you might as well take control over injection yourself, manually or through dagger.android.