Why is my field `null` after injection? How do I inject my object?
This is a Canonical Question because there are a lot of misconceptions about object initialization with Dagger 2.
If your question was flagged as a duplicate please read this post carefully and make sure to understand the difference between constructor injection and field injection.
I try to inject a Context
into my presenter, but I get a NullPointerException when trying to use it.
class MyPresenter {
@Inject Context context;
private MyView view;
@Inject
MyPresenter(MyView view) {
this.view = view;
}
}
My module looks like this
@Module
class MyModule {
@Provides
MyPresenter provideMyPresenter(MyView view) {
return new MyPresenter(view);
}
}
I inject the presenter in my Activity here:
class MyActivity extends Activity {
@Inject MyPresenter presenter;
@Override
public void onCreate(Bundle savedInstanceState) {
createMyActivityComponent().inject(this);
}
}
Solution 1:
The above includes both constructor and field injection, but neither done right. The example would behave the same if we removed all the @Inject
annotations from MyPresenter
since we're not using any of them.
@Provides
MyPresenter provideMyPresenter(MyView view) {
// no constructor injection, we create the object ourselves!
return new MyPresenter(view);
}
// also no mention anywhere of component.inject(presenter)
// so the fields won't be injected either
Make sure to use either constructor injection or field injection. Mixing both will usually indicate an error in your setup or understanding.
-
@Inject
on a field is a marker for field injection -
@Inject
on a constructor is a marker for constructor injection
This means your class should have either of
- a single
@Inject
on the constructor, or - a
@Inject
on all the fields to initialize, but none on the constructor!
Don't sprinkle @Inject
everywhere and expect things to work! Make sure to place the annotation where needed. Don't mix field and constructor injection!
Constructor injection should be favored over field injection as it creates an initialized and usable object. Field injection is to be used with Framework components where the Framework creates the objects. You have to manually call component.inject(object)
for field injection to be performed, or any annotated fields will be null when you try to use them.
Constructor Injection
As the name suggests you put your dependencies as parameters in the constructor. The annotation on the constructor tells Dagger about the object and it can then create the object for you by calling it with all the required dependencies. Dagger will also inject any annotated fields or methods after creating the object, but plain constructor injection should usually be favored as it doesn't hide any dependencies.
Dagger creating the object also means there is no need for a @Provides
method in your module that creates the object. All you need to do is add @Inject
to the constructor and declare the dependencies.
class MyPresenter {
private Context context;
private MyView view;
@Inject
MyPresenter(MyView view, Context context) {
this.view = view;
this.context = context
}
}
If you want to bind your implementation to an interface, there is still no need to create the object yourself.
@Module class MyModule {
@Provides
MyPresenter providePresenter(MyPresenterImpl presenter) {
// Dagger creates the object, we return it as a binding for the interface!
return presenter;
}
}
And there is even a shorter (and more performant) version of the above use-case:
@Module interface MyModule {
@Binds
MyPresenter providePresenter(MyPresenterImpl presenter)
}
Constructor injection should be your default way of using Dagger. Make sure that you don't call new
yourself or you misunderstood the concept.
Field Injection
There are times when you can't use constructor injection, e.g. an Activity in Android gets created by the Framework and you shouldn't override the constructor. In this case we can use field injection.
To use field injection you annotate all the fields that you want initialized with @Inject
and add a void inject(MyActivity activity)
method to the component that should handle the injection.
@Component
interface MyComponent {
void inject(MyActivity activity);
}
And somewhere in your code you have to call component.inject(myActivity)
or the fields will not be initialized. e.g. in onCreate(..)
void onCreate(..) {
// fields still null / uninitialized
myComponent.inject(this);
// fields are now injected!
// ...
}
Field injection is not transitive. Just because you inject an Activity this does not mean that Dagger will also inject the fields of the presenter it injected. You have to inject every object manually, which is one reason why you should favor constructor injection.
There are tools that help mitigate the boilerplate of creating components and injecting your objects like AndroidInjection.inject()
which will do this for you, but it still has to be done. Another example is AppInjector
which adds various lifecycle listeners to inject your Activities and Fragments, but it will still call AndroidInjection
which then creates your component and injects the object.
Make sure that you inject the object before using it and that there is no constructor annotated with @Inject
to avoid confusion.
What else?
There is also the lesser used method injection and of course Dagger can't inject third party libraries, which you have to construct and provide in your modules.