Dagger 2
Dagger implements the DI pattern without the burden of writing boilerplate code. Dagger 2 is the first to implement the full stack with generated code. The guiding principle is to generate code that mimics the code that a user might have hand-written to ensure that dependency injection is as simple, traceable and performant as it can be.
Declaring Dependencies:
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
It uses the javax.inject.Inject
annotation to identify which constructors and fields it is 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 constructor implies that the user will explicitly create an instance of the class. Dagger will inject the fields but will not construct objects. So, it is wise to add default constructor with @Inject
. Keep in mind that classes without @Inject
cannot be constructed by Dagger.
So in a normal scenario like above, Dagger will create an instance of a class by using constructor annotated with @Inject
and sets all the injectable fields which will be available for us to use. But if there is a restriction for us such that we cannot add @Inject
to the constructors, then dagger provides an alternative approach for DI ( using @Provides
). This happens when we need to inject third-party classes or when configurable objects must be configured.
@Provides
The return type determines the type of dependency this satisfies. ( method name does not matter )
The naming convention for the method 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 as well.
@Provides
methods 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 Graph
Dagger needs an access point to be able to access the graph formed by @Inject
and @Provides
annotated classes. We provide that by applying the @Component
annotation to an interface with methods that have no arguments and return the desired type. We provide all the modules to the @Component
and dagger will generate an implementation of these contracts.
@Component
Every type annotated with @Component must contain at least one abstract component method. Component methods may have any name but must have signatures that conform to either provision or members-injectioncontracts.
Provision methods
have no parameter and return an injected or provided type. Some example of provision method declarations are :
SomeType getSomeType();
Set<SomeType> getSomeTypes();
@PortNumber int getPortNumber()
Members-injection methods
have a single parameter and inject dependencies into each of the inject-annotated
fields and methods of the passed instance. A members-injection method may be void or return its single parameter as a convenience for chaining. Example :
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 will perform the same work as a members injection method. For example:
MembersInjector<SomeType> getSomeTypeMembersInjector();
where MembersInjector<T>
: Injects dependencies into the fields and methods on instances of type T
. Ignores 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 never need to use this method.
Scoped Bindings
@Singleton
on @Provides
or injectable class guarantees a single instance of the value for all clients.
@Reusable
can be used to limit the number of times an @Inject
constructed class is instantiated or @Provides
method is called and we don’t need a guarantee of the same exact instance being returned. This binding will not be associated with any single component. Each component will cache the instantiated object.
Using @Reusable
where mutable objects are returned is not recommended as it is expected to refer to the same instance.
Lazy Injections
For any binding T
, we can create a Lazy<T>
which defers instantiations until the first call to Lazy<T>'s get()
method.
class Test {
@Inject
Lazy<Actor> lazyActor;
public void doStunt() {
lazyActor.get().doNothing();
}
}
Provider Injections
Qualifiers
If type alone is not sufficient, we can use qualifier annotation. The sample below will be self-descriptive.
class Server {
@Inject
@Named("vodka")
Alcohol strongDrink;
@Inject
@Named("beer")
Alcohol lightDrink;
}
And the Qualified values will be declared as follows,
@Provides
@Named("vodka")
static Alcohol provideStringDrink() {
return new Alcohol(45);
}
@Provides
@Named("beer")
static Alcohol provideLightDrink() {
return new Alcohol(5);
}
Optional Bindings
Binding Instances
Sources