DFW IT Partner Team

Dagger 2

Dagger 2 dependency injection for Java and Android

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);
}

HAVE QUESTIONS ABOUT THIS?

Let's talk about your Odoo project.

Whether you're weighing your options or ready to move, we'll help you map out the right next step.