March 14, 2023
Dagger 2

Dagger implements the dependency injection pattern without the burden of writing boilerplate code. Dagger 2 was the first DI framework to implement the full stack with generated code, and its guiding principle is to generate code that mimics what a developer might have hand-written — keeping dependency injection simple, traceable, and performant.
Declaring the Dependency
Add Dagger to your Gradle build:
dependencies {
def dagger_version = "2.20"
... ...
... ...
implementation "com.google.dagger:dagger:$dagger_version"
implementation "com.google.dagger:dagger-android:$dagger_version"
implementation "com.google.dagger:dagger-android-support:$dagger_version"
// if you use the support libraries
annotationProcessor "com.google.dagger:dagger-android-processor:$dagger_version"
annotationProcessor "com.google.dagger:dagger-compiler:$dagger_version"
}@Inject
Dagger uses the javax.inject.Inject annotation to identify which constructors and fields it should be interested in.
Use @Inject to annotate the constructor that Dagger should use to create instances of a class:
class Test{
private final Sample sample;
@Inject
public Test(Sample sample){
this.sample=sample;
}
}Field Injection
class Test{
@Inject
private final Sample sample;
@Inject
private final AnotherSample anotherSample;
}Having @Inject-annotated fields but not a constructor implies that the caller will explicitly create an instance of the class themselves — Dagger will inject the fields but won't construct the object. So it's wise to add a default constructor annotated with @Inject too. Keep in mind that classes without @Inject anywhere cannot be constructed by Dagger at all.
In the normal case above, Dagger creates an instance of a class using the constructor annotated with@Inject and sets all the injectable fields, ready for us to use. But if there's a restriction that prevents us from adding @Inject to a constructor, Dagger offers an alternative: @Provides. This comes up when we need to inject third-party classes, or when a configurable object needs to be configured before it's handed out.
@Provides
The return type determines which dependency a @Provides method satisfies (the method name itself doesn't matter, though the convention is provideClass()):
@Provides
public Gson provideGson(){
GsonBuilder builder = new GsonBuilder();
builder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES);
return builder.create();
}@Provides methods can have dependencies of their own, but they must belong to a module — a class annotated with @Module:
@Module
public class RestModule {
@Singleton
@Provides
public Retrofit provideRetrofit(Gson gson,OkHttpClient okHttpClient){
return new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create(gson))
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.client(okHttpClient)
.build();
}
}Building the Graph
Dagger needs an access point to reach the graph formed by the @Inject- and@Provides-annotated classes. We provide that by applying the @Component annotation to an interface whose methods take no arguments and return the desired type. All the relevant modules are supplied to the @Component, and Dagger generates an implementation of the contract.
@Component
Every type annotated with @Component must contain at least one abstract component method. Component methods can have any name, but their signatures must conform to either aprovision or a members-injection contract.
Provision methods take no parameters and return an injected or provided type, for example:
SomeType getSomeType();
Set<SomeType> getSomeTypes();
@PortNumber int getPortNumber()Members-injection methods take a single parameter and inject dependencies into each of the@Inject-annotated fields and methods of the passed instance. A members-injection method can be void, or return its single parameter as a convenience for chaining:
void injectSomeType(SomeType someType);
SomeType injectAndReturnSomeType(SomeType someType);A method with no parameters that returns a MembersInjector is equivalent to a members-injection method. Calling MembersInjector.injectMembers(T) on the returned object performs the same work as a members-injection method, for example:
MembersInjector<SomeType> getSomeTypeMembersInjector();where MembersInjector<T> injects dependencies into the fields and methods of instances of type T, ignoring the presence or absence of an injectable constructor:
injectMembers(T instance)Whenever a component creates an instance, it performs this injection automatically — after first performing constructor injection — so if you're able to let the component create all your objects for you, you'll rarely need to call this method yourself.
Scoped Bindings
@Singleton on a @Provides method or an injectable class guarantees a single instance of the value across all clients.
@Reusable can be used to limit how many times an @Inject-constructed class is instantiated, or an @Provides method is called, when we don't need a guarantee of the exact same instance being returned every time. This binding isn't associated with any single component — each component simply caches the instantiated object it creates. Using @Reusable where mutable objects are returned isn't recommended, since callers may expect to be handed the same instance.
Lazy Injections
For any binding T, we can create a Lazy<T> that defers instantiation until the first call to its get() method:
class Test {
@Inject
Lazy<Actor> lazyActor;
public void doStunt() {
lazyActor.get().doNothing();
}
}Qualifiers
If type alone isn't sufficient to distinguish a binding, we can use a qualifier annotation. The following example is fairly self-descriptive:
class Server {
@Inject
@Named("vodka")
Alcohol strongDrink;
@Inject
@Named("beer")
Alcohol lightDrink;
}The qualified values are then declared like this:
@Provides
@Named("vodka")
static Alcohol provideStringDrink() {
return new Alcohol(45);
}
@Provides
@Named("beer")
static Alcohol provideLightDrink() {
return new Alcohol(5);
}