Development Technology

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 @Injectcannot 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 @Componentannotation 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 @Providesmethod 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

Daggers Official Users Guide

Custom Scopes

medium.com – Dagger’s Missing Guide

medium.com – – Retrofit-Dagger-RxJava2 MVVM

Author

Viram Layman

Leave a comment

Your email address will not be published. Required fields are marked *