Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Dependency Injection Using Dagger 2

Dependency Injection Using Dagger 2

Mustafa Berkay Mutlu

October 26, 2017
Tweet

More Decks by Mustafa Berkay Mutlu

Other Decks in Programming

Transcript

  1. DEPENDENCY INJECTION USING 

    DAGGER 2
    MUSTAFA BERKAY MUTLU
    Photo by Shashank Sahay on Unsplash

    View Slide

  2. DEPENDENCY
    A dependency is an object that can be used.
    public class SomeClass {

    private final Object myObject;


    public SomeClass() {

    myObject = Factory.getObject();

    }

    }

    View Slide

  3. PROBLEMS WHEN UNIT TESTING
    You need to intercept the Factory call mock myObject in order to write unit
    tests.
    public class SomeClass {

    private final Object myObject;


    public SomeClass() {

    myObject = Factory.getObject(); // Hard to replace this

    }

    }

    View Slide

  4. This is one style of dependency injection, via the constructor.
    public class SomeClass {

    private final Object myObject;


    public SomeClass(Object myObject) {

    this.myObject = myObject;

    }

    }

    View Slide

  5. Basically, instead of having your objects creating a dependency or asking a
    factory object to make one for them,
    you pass the needed dependencies in to the object externally.
    .. or you can use a dependency injector that builds the dependency graph for
    you.
    INJECTING DEPENDENCIES

    View Slide

  6. DAGGER 1
    • Made by Square
    • Annotation-based code generation (JSR-330)
    • Uses reflection
    • Graph composition at runtime 
    • Deprecated

    View Slide

  7. DAGGER 2
    • Maintained by Google
    • Replaces Dagger 1
    • Reflection-free
    • Fully static, compile-time dependency injection
    • Better generated code

    View Slide

  8. PERFORMANCE COMPARISON OF DI LIBRARIES
    Mar 7, 2016 data, by NimbleDroid [1]
    Average duration of 5455 injections:
    Roboguice 3923 ms
    Dagger 1 154 ms
    Dagger 2 62 ms

    View Slide

  9. METHOD COUNT
    “Dagger assumes that users on Android will use ProGuard.”
    - Dagger 2

    View Slide

  10. ADDING DAGGER TO AN ANDROID PROJECT

    dependencies {

    compile “com.google.dagger:dagger:2.x”

    annotationProcessor “com.google.dagger:dagger-compiler:2.x”

    }

    View Slide

  11. The best classes in any application are the ones that do stuff:
    the BarcodeDecoder, the KoopaPhysicsEngine, and the AudioStreamer.
    These classes have dependencies; perhaps
    a BarcodeCameraFinder, DefaultPhysicsEngine, and an HttpStreamer.
    DAGGER 2

    View Slide

  12. To contrast, the worst classes in any application are the ones that take up space
    without doing much at all:
    the BarcodeDecoderFactory, the CameraServiceLoader, and
    the MutableContextWrapper.
    These classes are the clumsy duct tape that wires the interesting stuff together.

    View Slide

  13. Dagger is a replacement for these Factory classes that implements
    the dependency injection design pattern without the burden of writing the
    boilerplate.

    View Slide

  14. By building on standard javax.inject annotations, each class is easy to test.
    You don’t need a bunch of boilerplate just to swap
    the RpcCreditCardService out for a FakeCreditCardService.

    View Slide

  15. Dependency injection isn’t just for testing. It also makes it easy to
    create reusable, interchangeable modules.
    You can share the same AuthenticationModule across all of your apps.
    You can run DevLoggingModule during development
    and ProdLoggingModule in production to get the right behavior in each
    situation.

    View Slide

  16. DECLARING DEPENDENCIES
    • @Inject annotation required
    • Constructor, field and method injection

    View Slide

  17. When a new instance is requested, Dagger will obtain the required parameters
    values and invoke this constructor.
    class Thermosiphon implements Pump {

    private final Heater heater;


    @Inject

    Thermosiphon(Heater heater) {

    this.heater = heater;

    }
    }

    View Slide

  18. Dagger can inject fields directly.
    Add a no-argument constructor with the @Inject annotation to indicate that Dagger may create
    instances as well.
    class CoffeeMaker {

    @Inject Heater heater;

    @Inject Pump pump;


    @Inject

    public CoffeeMaker() {

    }


    ...

    }

    View Slide

  19. PROVIDING DEPENDENCIES
    • Modules are classes whose methods provide dependencies
    • @Module on the class
    • @Provides on each provider method

    View Slide

  20. @Module

    public class SingletonModule {

    private final ApiConfiguration apiConfiguration;


    public SingletonModule(ApiConfiguration apiConfiguration) {

    this.apiConfiguration = apiConfiguration;

    }


    @Provides

    Services provideServices(@NonNull Retrofit retrofit) {

    return retrofit.create(Services.class);

    }


    @Provides

    Retrofit provideRetrofit(@NonNull OkHttpClient okHttpClient) {

    return new Retrofit.Builder()

    .baseUrl(apiConfiguration.getBaseUrl())

    .addConverterFactory(GsonConverterFactory.create())

    .client(okHttpClient)

    .build();

    }


    @Provides

    OkHttpClient provideOkHttpClient() {

    return new OkHttpClient.Builder().build();

    }

    }


    View Slide

  21. COMPONENT
    @Component(modules = {SingletonModule.class, ApiModule.class})

    public interface SingletonComponent {


    Services getServices();


    void inject(SettingsFragment settingsFragment);


    }

    View Slide

  22. ApiConfiguration apiConfiguration = IoUtil.initApiConfiguration();
    singletonComponent = DaggerSingletonComponent.builder()

    .singletonModule(new SingletonModule(apiConfiguration))
    .apiModule(new ApiModule())

    .build();
    BUILDING THE GRAPH

    View Slide

  23. ApiConfiguration apiConfiguration = IoUtil.initApiConfiguration();
    singletonComponent = DaggerSingletonComponent.builder()

    .singletonModule(new SingletonModule(apiConfiguration))
    .apiModule(new ApiModule())

    .build();
    Dagger will create modules with default constructors for you, so you don’t
    need to create them.

    View Slide

  24. ApiConfiguration apiConfiguration = IoUtil.initApiConfiguration();
    singletonComponent = DaggerSingletonComponent.builder()

    .singletonModule(new SingletonModule(apiConfiguration))

    .build();

    View Slide

  25. public class MyApp extends Application {

    private SingletonComponent singletonComponent;


    @Override

    public void onCreate() {

    super.onCreate();


    singletonComponent = DaggerSingletonComponent.builder()

    .singletonModule(new SingletonModule())

    .build();

    }


    public SingletonComponent getSingletonComponent() {

    return singletonComponent;

    }

    }

    View Slide

  26. public class SettingsFragment extends Fragment {


    @Inject

    Services services;

    @Override

    public void onCreate(@Nullable Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);


    ((MyApp) getActivity().getApplication())

    .getSingletonComponent()

    .inject(this);

    }
    }

    View Slide

  27. DAGGER API
    • @Module + @Provides: mechanism for providing dependencies
    • @Inject: mechanism for requesting dependencies
    • @Component: bridge between modules and injections

    View Slide

  28. SCOPES
    • @Singleton is the largest scope
    • Custom annotations for semantic clarity
    • Dagger will inject the same instance inside the scope (local singletons)
    • If we don’t use scopes then Dagger will create new dependencies every time
    we inject

    View Slide

  29. Annotate an @Provides method or injectable class with @Singleton. The graph will use a single instance of the value
    for all of its clients.
    @Module

    public class ApiModule {


    @Singleton

    @Provides

    Services provideServices(@NonNull Retrofit retrofit) {

    return retrofit.create(Services.class);

    }


    ...


    }

    View Slide

  30. Annotate an @Provides method or injectable class with @Singleton. The graph will use a single instance of the value
    for all of its clients.
    @Module

    public class ApiModule {


    @Singleton

    @Provides

    Services provideServices(@NonNull Retrofit retrofit) {

    return retrofit.create(Services.class);

    }


    ...


    }

    View Slide

  31. The @Singleton annotation on an injectable class also serves
    as documentation.
    @Singleton

    class CoffeeMaker {

    ...

    }

    View Slide

  32. COMPONENT SCOPES
    The components themselves need to declare which scope they intend to
    represent.
    @Component(modules = DripCoffeeModule.class)

    @Singleton

    interface CoffeeShop {

    CoffeeMaker maker();

    }

    View Slide

  33. LAZY INJECTIONS
    class GrindingCoffeeMaker {

    @Inject

    Lazy lazyGrinder;


    public void brew() {

    while (needsGrinding()) {

    // Grinder created once on first call to .get() and cached.

    lazyGrinder.get().grind();

    }

    }

    }

    View Slide

  34. QUALIFIERS
    Sometimes the type alone is insufficient to identify a dependency.
    @Provides

    @Named("hot plate")

    Heater provideHotPlateHeater() {

    return new ElectricHeater(70);

    }


    @Provides

    @Named("water")

    Heater provideWaterHeater() {

    return new ElectricHeater(93);

    }
    class ExpensiveCoffeeMaker {

    @Inject

    @Named("water")

    Heater waterHeater;


    @Inject

    @Named("hot plate")

    Heater hotPlateHeater;


    ...

    }

    View Slide

  35. COMPILE-TIME VALIDATION
    The Dagger annotation processor is strict and will cause a compiler error if any
    bindings are invalid or incomplete.
    @Module

    class DripCoffeeModule {

    @Provides 

    Heater provideHeater(Executor executor) {

    return new CpuHeater(executor);

    }

    }
    [ERROR] error: java.util.concurrent.Executor cannot be provided without
    an @Provides-annotated method.

    View Slide

  36. COMPILE-TIME CODE GENERATION
    CoffeeMaker_Factory.java and CoffeeMaker_MembersInjector.java?
    You shouldn’t need to use them directly.
    The only generated types you should refer to in your code are the ones
    prefixed with Dagger for your component.

    View Slide

  37. MORE CONCEPTS
    • Android Injection (Dagger 2.10+)
    • Subcomponents
    • Component dependencies
    • Testing and module overrides

    View Slide

  38. SAMPLE
    Instant Apps + Kotlin + Dagger + MVP:
    github.com/mustafaberkaymutlu/uv-index

    View Slide

  39. REFERENCES
    1. http://blog.nimbledroid.com/2016/03/07/performance-of-dependency-
    injection-libraries.html

    View Slide

  40. QUESTIONS?

    View Slide