Dagger 2

why and how

We don't need it

the objects are given their dependencies at creation time by some external entity that coordinates each object in the system. In other words, dependencies are injected into objects

Spring in action 2 Craig Walls

Goal

  • Testing isolation passing mocks
  • Loose coupling
  • Lifecycle
  • Lazy
  • Inversion Of Control (Dependency Injection)
  • Dependency Inversion
  • Injecting vs Instantiating
  • Lazy

Inversion of control

the object finds the objects it depends on by itself and then calls them

Dependency Injection

an external object provides these dependencies when another object needs them for it's creation

Dependency Inversion

  • High-level modules should not depend on low-level modules. Both should depend on abstractions.
  • Abstractions should not depend on details. Details should depend on abstractions.
  • etc.

Entire app?

do we need it... ?

JSR-330

  • @Inject

Java Specification Requests

DI Libraries

  • Spring
  • Guice
  • PicoContainer
  • CDI
  • OpenEJB
  • Weld
  • Needle
  • etc...

Cucumber

Provider Interface

Typically implemented by an injector

Interface Provider<T>

Compared to injecting T directly, injecting Provider<T> enables:

  • retrieving multiple instances.
  • lazy or optional retrieval of an instance.
  • breaking circular dependencies.
  • abstracting scope so you can look up an instance in a smaller scope from an instance in a containing scope.

Example

 class Car {
     @Inject Car(Provider<Seat> seatProvider) {
       Seat driver = seatProvider.get();
       Seat passenger = seatProvider.get();
       ...
     }
   }

get()
    Provides a fully-constructed and injected instance of T.

Scope Interface

A scope annotation applies to a class containing an injectable constructor, and governs how the injector reuses instances of the type

By default, if no scope annotation is present, the injector creates an instance (by injecting the type's constructor), uses the instance for one injection, and then forgets it.

The implementation of the scope itself is left up to the injector

Annotating scope annotations with @Scope helps the injector detect the case where a programmer used the scope annotation on a class but forgot to configure the scope in the injector. A conservative injector would generate an error rather than not apply a scope.

Example

 

@Scope
@Retention(RetentionPolicy.RUNTIME)   
public @interface RequestScoped {}
@RequestScoped
@Component(modules = { ... })
  public interface CoffeeComponent {
    CoffeeMaker maker();
  }

usage:

declaration:

@Module and @Provides: mechanism for providing dependencies

@Inject: mechanism for requesting dependencies

@Component: bridge between modules and injectors

Providing dependencies

@Module
public class NetModule {
    @Provides 
    @Singleton
    TSHttpClient provideHttpClient() {  
       return new HttpClient(...);
   }
}

Providing dependencies

@Module
public class LibraryModule {
    @Provides 
    @Singleton
    List<Specialties> provideSpecialties(TSHttpClient httpClient) {  
       //httpClient fetch
       return specialties;
   }
}

Requesting dependencies

@Inject annotation

  • Constructor
  • Field
  • Method

Constructor Injection

@Singleton
class LibraryUsecase {
    private final LibraryModule libModule;

    @Inject
    public LibraryUsecase(LibraryModule libModule) {
       this.libModule = libModule;
}

Available for downstream injection

Field Injection

class LibraryActivity extends Activity {
    @Inject LibraryModule libModule;

    /..
}

Method Injection

class LibraryUsecase {
    /..
    private void enableStreaming(Streaming streaming){
        streaming.subscribe(this);
    }
    /..
}

Combining the two

Components

@Singleton //modules are singletons
@Component(modules {
    NetModule.class, LibraryModule.class })
public interface LibraryComponent {
    private final LibraryModule libModule;
}

Instantiating the component

public class MyApp extends Application {
    private NetComponent mNetComponent;
    @Override
    public void onCreate() {
        super.onCreate();
        mNetComponent = DaggerNetComponent.builder()
                // list of modules that are part of this
                //component need to be created here too
                .appModule(new AppModule(this)) 
                .netModule(new NetModule( 
                              "https://api.github.com"))
                .build();
    }
}

Drawbacks

  • Steep learning curve
  • Hard to understand the errors
  • Sometimes lengthens startup time

Dagger 2

By chronvas

Dagger 2

  • 160