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

Dependency Injection Using Dagger 2

Dependency Injection Using Dagger 2

Da49bf507d07666987c1546e894dee8d?s=128

Mustafa Berkay Mutlu

October 26, 2017
Tweet

Transcript

  1. DEPENDENCY INJECTION USING 
 DAGGER 2 MUSTAFA BERKAY MUTLU Photo

    by Shashank Sahay on Unsplash
  2. DEPENDENCY A dependency is an object that can be used.

    public class SomeClass {
 private final Object myObject;
 
 public SomeClass() {
 myObject = Factory.getObject();
 }
 }
  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
 }
 }
  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;
 }
 }
  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
  6. DAGGER 1 • Made by Square • Annotation-based code generation

    (JSR-330) • Uses reflection • Graph composition at runtime  • Deprecated
  7. DAGGER 2 • Maintained by Google • Replaces Dagger 1

    • Reflection-free • Fully static, compile-time dependency injection • Better generated code
  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
  9. METHOD COUNT “Dagger assumes that users on Android will use

    ProGuard.” - Dagger 2
  10. ADDING DAGGER TO AN ANDROID PROJECT 
 dependencies {
 compile

    “com.google.dagger:dagger:2.x”
 annotationProcessor “com.google.dagger:dagger-compiler:2.x”
 }
  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
  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.
  13. Dagger is a replacement for these Factory classes that implements

    the dependency injection design pattern without the burden of writing the boilerplate.
  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.
  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.
  16. DECLARING DEPENDENCIES • @Inject annotation required • Constructor, field and

    method injection
  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;
 } }
  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() {
 }
 
 ...
 }
  19. PROVIDING DEPENDENCIES • Modules are classes whose methods provide dependencies

    • @Module on the class • @Provides on each provider method
  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();
 }
 }

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


    Services getServices();
 
 void inject(SettingsFragment settingsFragment);
 
 }
  22. ApiConfiguration apiConfiguration = IoUtil.initApiConfiguration(); singletonComponent = DaggerSingletonComponent.builder()
 .singletonModule(new SingletonModule(apiConfiguration)) .apiModule(new

    ApiModule())
 .build(); BUILDING THE GRAPH
  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.
  24. ApiConfiguration apiConfiguration = IoUtil.initApiConfiguration(); singletonComponent = DaggerSingletonComponent.builder()
 .singletonModule(new SingletonModule(apiConfiguration))
 .build();

  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;
 }
 }
  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);
 } }
  27. DAGGER API • @Module + @Provides: mechanism for providing dependencies

    • @Inject: mechanism for requesting dependencies • @Component: bridge between modules and injections
  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
  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);
 }
 
 ...
 
 } 

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

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

    documentation. @Singleton
 class CoffeeMaker {
 ...
 }
  32. COMPONENT SCOPES The components themselves need to declare which scope

    they intend to represent. @Component(modules = DripCoffeeModule.class)
 @Singleton
 interface CoffeeShop {
 CoffeeMaker maker();
 }
  33. LAZY INJECTIONS class GrindingCoffeeMaker {
 @Inject
 Lazy<Grinder> lazyGrinder;
 
 public

    void brew() {
 while (needsGrinding()) {
 // Grinder created once on first call to .get() and cached.
 lazyGrinder.get().grind();
 }
 }
 }
  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;
 
 ...
 }
  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.
  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.
  37. MORE CONCEPTS • Android Injection (Dagger 2.10+) • Subcomponents •

    Component dependencies • Testing and module overrides
  38. SAMPLE Instant Apps + Kotlin + Dagger + MVP: github.com/mustafaberkaymutlu/uv-index

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

  40. QUESTIONS? ☝