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

A (K)night with Dagger

Chintan Soni
November 05, 2017

A (K)night with Dagger

Presented this talk at GDG DevFest Ahmedabad 2017.
Dagger2 is been here since 2015, though average developers are not able to get the underlying architecture and its usage. This talk covers deep understanding about annotation processors, how Dagger2 works under the hood, how to rebuild current application architecture with Dagger2 and detailing about Dagger2 annotations.

Find original presentation with animations at: https://goo.gl/N3YYZQ

Chintan Soni

November 05, 2017
Tweet

More Decks by Chintan Soni

Other Decks in Technology

Transcript

  1. A (K)night
    with Dagger
    Slicing problems of
    newbies with DI

    View Slide

  2. Why Dagger is important nowadays ?

    View Slide

  3. Like you love to watch Thor
    with his Hammer

    View Slide

  4. Interviewers watch for Dagger in
    you

    View Slide

  5. What is Dependency Injection ?

    View Slide

  6. Please don’t react this way!

    View Slide

  7. Ok, What is Dependency ?

    View Slide

  8. View Slide

  9. Dependency
    ● Class Black needs object from class Red,
    Green and Blue, to fulfil its purpose.
    ● It implies that class Black is dependent on
    Red, Green and Blue.
    ● So, Dependency is an object that can be
    used.

    View Slide

  10. And, What is Injection ?

    View Slide

  11. To inject something..

    View Slide

  12. So, what is dependency injection ?

    View Slide

  13. View Slide

  14. Dependency Injection
    ● Injection: Passing of one or more objects
    to the depending class
    ● Dependency Injection: A form of
    Inversion of Control, a design principle,
    which say that any class should be getting
    its dependencies from outside.
    ● In other words, class should not be
    instantiating other class within it, but rather
    there should be another configuration
    class which can provide with those
    instances.

    View Slide

  15. So, what does that mean ? You can...
    ● Write testable code
    ● Decouple class and its dependency
    ● Design maintainable and reusable structure

    View Slide

  16. Sounds interesting! Okay then..

    View Slide

  17. Let’s dive into DI

    View Slide

  18. What is Reflection ?

    View Slide

  19. Reflection...
    ● Enables the code with ability to examine or modify the Runtime behavior of
    class in runtime.
    ● Java Reflection Api was introduced in Java 1.5
    ● Drawbacks:
    ○ Performance Overhead
    ○ Exposure of Internals
    ○ Unexpected and Untraceable Crashes due to dependency resolution at runtime

    View Slide

  20. Example of Reflection
    ● Packages that provide classes for reflection:
    ○ java.lang
    ○ java.lang.reflect
    ● Class class:
    ○ MainActivity.class.getSimpleName() returns “MainActivity” using Reflection

    View Slide

  21. Let's see, who used Reflection ?

    View Slide

  22. History of Dagger1
    ● Square Inc.’s Dagger1 is a DI Framework which was purely dependent on
    Java Reflection Api
    ● Typically, you are not gonna use DI for injecting just one dependency
    ● So, more injections implies more performance overhead
    ● Also, error tracing was extremely difficult

    View Slide

  23. Sometimes, it may lead to...

    View Slide

  24. But, thanks to Google
    Google undertook this project and made revolutionary
    enhancements in the underlying mechanism of Dagger1

    View Slide

  25. And Dagger2 was born...

    View Slide

  26. Let’s Get Started with Dagger 12
    Dagger 1 Dagger 2

    View Slide

  27. What is Annotation ?

    View Slide

  28. Annotation is...
    ● A class of metadata, introduced in Java 1.5, that can be associated with
    class, methods, fields, and even other annotations.
    ● Those meta data can be accessed at runtime via Reflection.
    ● But we were not known to the same capability of reading metadata at compile
    time via Annotation Processors.

    View Slide

  29. What do Annotation Processors do ?

    View Slide

  30. Annotation Processors...
    ● Are code generators that eliminate boilerplate code by generating code for
    you at Compile Time. Really..!!
    ● No performance Overheads at all (Compile Time nature).
    ● Even you can create your own Annotations and a Processor for it using this
    tutorial: The 10 step guide to annotation processing in android studio

    View Slide

  31. How Dagger 2 differs from Dagger 1
    ● Works purely on Annotation Processor
    ● “Which client is dependent on what objects, that is resolved at compilation
    stage.”
    ● Hence, Dagger2 is free from Performance Overhead. Also, Dagger2 errors are
    highly traceable.

    View Slide

  32. Modes of Dependency Injection

    View Slide

  33. Modes of DI
    ● Java Specification Request 330 (JSR 330) has defined standard Java
    Annotations for describing dependencies of a class.
    ● Modes of DI:
    ○ Constructor Injection: Injecting the constructor parameters
    ○ Field Injection: Injecting the member variable (Required that it must not be private)
    ○ Method Injection: Injecting the method parameter

    View Slide

  34. By the way… Some Annotation Processors..
    Jake Wharton’s ButterKnife is an
    Annotation Processor.
    @Bind(R.id.textView)
    TextView mTextView;
    Android Architecture Component’s Room
    Persistence Library is an Annotation Processor
    @Entity
    public class User {...}
    @Dao
    public interface UserDao {...}
    @Database(entities = {User.class}, version = 1)
    public abstract class AppDatabase extends
    RoomDatabase {...}

    View Slide

  35. Let’s learn to DI using Dagger2

    View Slide

  36. Objective
    Display List of Users
    using Random User Api

    View Slide

  37. RandomUserApplic
    ation.java

    View Slide

  38. But I am really not happy with it.
    ● Firstly, code is ought to be in serialized in some way. Every objects need to be
    initialized in some order.
    ● Secondly, Code looks very clumsy
    ● And lastly, Code is not test friendly

    View Slide

  39. Picasso is breaking the rule of testability.

    View Slide

  40. Two major problems to be solved..
    ● Ideal way of dealing with clumsy initializations ?
    ● Make our code more testable ?

    View Slide

  41. Initial Setup Towards Dagger 2 implementation
    ● Identify the classes who secretly depend on objects.
    ● Provide dependency of such objects through constructor arguments
    ● Do necessary object creations at one level, let’s say, at Application Level.
    ● Example: We are gonna provide single point of accessing Picasso
    instance. So, class dependent on Picasso, should be having Picasso as
    constructor arguments.

    View Slide

  42. View Slide

  43. Dependency
    Graph

    View Slide

  44. Make sure to add below Dependencies
    dependencies {
    implementation 'com.google.dagger:dagger:2.11'
    annotationProcessor 'com.google.dagger:dagger-compiler:2.11'
    }

    View Slide

  45. Annotations with Dagger2
    ● @Module and @Provides: define classes and methods which provide
    dependencies
    ● @Inject: request dependencies. Can be used on a constructor, a field, or a
    method
    ● @Component: enable selected modules and used for performing dependency
    injection

    View Slide

  46. Let's start with Component

    View Slide

  47. Component is...
    ● A public interface to your dependency graph
    ● Best Practise: Exposes only your top level dependency. Internal dependencies
    should remain under the hood.
    ● Go ahead creating a component RandomUserComponent

    View Slide

  48. @Component: Tells Dagger that this interface is a Dagger Component and not an
    ordinary interface. Dagger will generate a subclass of this component with
    implementation of getPicasso() and getRandomUserService()

    View Slide

  49. View Slide

  50. But how will Component know from
    where to get the dependencies of
    Picasso and RandomUserService ?

    View Slide

  51. Modules for dependencies

    View Slide

  52. Modules...
    ● provide those under the hood dependencies to the outermost dependency,
    like Picasso and RandomUserService.
    ● Go ahead with moving my code from RandomUserApplication class to these
    modules as OkHttpClientModule, PicassoModule,
    RandomUserServiceModule.
    ● @Provides: If any method in module provides dependency, it should be
    annotated with @Provides

    View Slide

  53. View Slide

  54. View Slide

  55. View Slide

  56. View Slide

  57. We are all set with our modules,
    But,
    PicassoModule and OkHttpClientModule
    are incomplete without Context…

    View Slide

  58. Manage External Dependencies
    ● External Dependencies: Dependencies on which our graph itself is dependent
    on.
    ● Our modules, now, are just lacking with the dependency of Context.
    ● Go ahead with Creating a ContextModule

    View Slide

  59. ● External Dependencies to our graph or module can be of Context, Activity,
    Service etc.
    ● They are external because WE NEVER CREATE CONTEXT / ACTIVITY / SERVICE
    INSTANCE BY NEW KEYWORD…
    ● Remember this…

    View Slide

  60. View Slide

  61. But how do I pass Context into other
    modules ?

    View Slide

  62. Modules with includes attribute

    View Slide

  63. Modules with includes attribute
    Includes attribute: includes other module dependency to the current module
    So, my module’s @Module annotation got updated with includes attribute as:
    @Module(includes = OkHttpClientModule.class)
    public class RandomUserServiceModule {}
    @Module(includes = OkHttpClientModule.class)
    public class PicassoModule {}
    @Module(includes = ContextModule.class)
    public class OkHttpClientModule {}

    View Slide

  64. View Slide

  65. By now,
    our modules are connected and can
    communicate to each other.
    We are simply remaining to tell our
    Component that
    which Modules you need to depend on..

    View Slide

  66. Component annotation has modules attribute
    ● Modules attribute: provides a way to tell component about its dependent
    modules.
    ● So, my component’s @Component annotation got updated with modules
    attribute as:
    @Component(modules = {RandomUserServiceModule.class,
    PicassoModule.class})
    public interface RandomUserApplicationComponent {
    ...
    }

    View Slide

  67. View Slide

  68. Now, hit gradle build…

    View Slide

  69. And if you are lucky...
    ● Dagger generates a class with Builder Design Pattern
    DaggerRandomUserApplicationComponent which now provides a way to
    initialize any component with modules associated with it.
    ● Magic of Dagger: For a particular component, you only need to pass
    modules that depend on external dependencies, you can skip providing a
    component with internal dependent modules.

    View Slide

  70. Now, let’s look at our application
    class…

    View Slide

  71. View Slide

  72. But there’s one problem...

    View Slide

  73. Every time you call Component#build()
    ● Dagger by default, creates new instances of every dependency to be
    injected.
    ● Why Dagger doesn’t understand that I just need a single instance to be
    created for Picasso ?
    ● How do we tell Dagger to create just a single instance for any dependency ?

    View Slide

  74. There should be something to control instance
    creations

    View Slide

  75. Limit the instances by Scope

    View Slide

  76. Scope...
    ● Annotation informs Dagger to share the same instance every time
    Component.build() will be called.
    ● It will make dependencies work as a Singleton for that particular Component.

    View Slide

  77. This is how we use
    @Scope
    @Scope
    @Retention(RetentionPolicy.CLA
    SS)
    public @interface
    RandomUserApplicationScope {
    }
    This is how we will use our customly created Scope:
    ● Firstly, we have to put it at component level:
    @RandomUserApplicationScope
    @Component(modules = {RandomUserServiceModule.class,
    PicassoModule.class})
    public interface RandomUserApplicationComponent {}
    ● Then, put it on every method with @Provides, that needs to act
    as Singleton. Usually, we need just single instance for
    OkhttpClient, Picasso, etc:
    @Provides
    @RandomUserApplicationScope
    public Picasso picasso(Context context, OkHttp3Downloader
    okHttp3Downloader) {}
    @Provides
    @RandomUserApplicationScope
    public Retrofit retrofit(OkHttpClient okHttpClient,
    GsonConverterFactory gsonConverterFactory) {}

    View Slide

  78. Typically, we need two type of Contexts:
    Application and Activity. We can have
    ActivityModule to provide Activity
    context.

    View Slide

  79. ● But Dagger gets confused which Context to use because it has got two modules
    that has methods that provide Context and it will throw error on build.
    ● How can we tell Dagger that this dependency should use Application Context or
    Activity Context ?

    View Slide

  80. Named Annotations comes in...

    View Slide

  81. @Named annotation does this job
    We can specify @Named(“”) annotation as:
    In ContextModule:
    @Provides
    @RandomUserApplicationScope
    @Named("application_context")
    public Context context() {
    return context.getApplicationContext();
    }
    In ActivityModule:
    @Provides
    @RandomUserApplicationScope
    @Named("activity_context")
    public Context context() {
    return context;
    }

    View Slide

  82. Tell Dagger to use Application context as
    In OkHttpModule:
    @Provides
    @RandomUserApplicationScope
    public File
    file(@Named("application_context")
    Context context) {}
    In PicassoModule:
    @Provides
    @RandomUserApplicationScope
    public Picasso
    picasso(@Named("application_conte
    xt") Context context,
    OkHttp3Downloader
    okHttp3Downloader) {}

    View Slide

  83. Alternative to Named Annotations

    View Slide

  84. @Qualifier is of my type
    Create an interface as:
    @Qualifier
    public @interface ApplicationContext {
    }
    @Provides
    @RandomUserApplicationScope
    @ApplicationContext
    public Context context() {
    return context;
    }
    Use it in OkHttpModule and PicassoModule:
    @Provides
    @RandomUserApplicationScope
    public File file(@ApplicationContext Context
    context) {}
    @Provides
    @RandomUserApplicationScope
    public Picasso picasso(@ApplicationContext
    Context context, OkHttp3Downloader
    okHttp3Downloader) {}

    View Slide

  85. View Slide

  86. Till now...
    ● We have created Application Level Dependencies
    ● But what about some dependencies that we need just on Activity Level ?
    ● Activity Life Cycle runs separate from Application Life Cycle
    ● So, dependencies created within Activity should be destroyed with Activity
    itself.
    ● Let’s understand Multiple Components and communication between them

    View Slide

  87. Creating Multiple Components and make them talk
    ● Best practises: When you are injecting dependencies into clients who have
    life cycle different from where dependencies are coming, it's better to
    create a separate component and modules for that client.

    View Slide

  88. Firstly, Let’s have activity-level scope
    @Scope
    public @interface MainActivityScope {
    }
    ● We will apply this scope to component and modules we will create next
    ● So, let’s have a MainActivityComponent and make it talk to
    RandomUserApplicationComponent for dependencies they are missing.

    View Slide

  89. ● Dependencies attribute: It tells Dagger that while creating component, if you are
    looking for any other dependencies, you can look into components specified in
    the dependencies attribute.
    ● So, now MainActivityComponent can look for dependencies into
    RandomUserApplicationComponent

    View Slide

  90. View Slide

  91. And we have activity-scoped MainActivityModule

    View Slide

  92. View Slide

  93. Now, check changes in Activity and Application class

    View Slide

  94. View Slide

  95. View Slide

  96. A lot more manageable code base has
    been established by now...
    That’s it…???!!!

    View Slide

  97. What if I have 50 dependencies in your component ?
    Would write 50 different lines ?
    randomUserService = mainActivityComponent.randomUserService();
    randomUserAdapter = mainActivityComponent.randomUserAdapter();
    ...
    Well, I am lazy at this and we are already learning lazy loading of objects.

    View Slide

  98. You might think..
    “Ohh..
    I don’t care.
    I am good at writing utility classes... ”

    View Slide

  99. Well, This is not my style… Guess why...

    View Slide

  100. @Inject is waiting for you...
    ● @Inject : JSR 330 (Java Specification Requests) provides this annotation
    ● Instead of telling Dagger, that I want RandomUserAdapter and
    RandomUserService instances, let Dagger react upon fields with @Inject
    annotations.
    ● But to use the magic of @Inject, we will need to modify our code a little.

    View Slide

  101. Modify MainActivityComponent as
    ● Remove methods RandomUserAdapter and RandomUserService.
    ● Instead, create a method with MainActivity as parameter.

    View Slide

  102. Add @Inject to fields

    View Slide

  103. How will this work ???
    ● Now, when Dagger finds a method in MainActivityComponent with void as
    return type, “Omg… there’s no return type…!!! There must be something I need
    to look into this class. Ohh yes, I can see fields annotated with @Inject, let me
    initialize them”
    ● And you are done.. You can run your code

    View Slide

  104. I can still improve it even more, with Constructor
    Injection...

    View Slide

  105. MainActivity is passed as a constructor parameter...

    View Slide

  106. Let’s make MainActivity injectable

    View Slide

  107. ● In RandomUserAdapter, pass MainActivity as constructor parameter
    ● Annotate it with @Inject

    View Slide

  108. Now, hit gradle build again…

    View Slide

  109. Mission
    Accomplished!!!
    Link to Dagger 2 Example:
    https://goo.gl/FsSL7n

    View Slide

  110. That’s all with
    Dagger

    View Slide

  111. Any Queries ???

    View Slide

  112. “Never say
    I don’t know it,
    say
    I haven’t started yet”
    - Chintan Soni

    View Slide

  113. Chintan Soni
    Senior Android Developer @
    Simform Solutions Pvt. Ltd.
    Follow me on:
    FB: chintansoni202
    Twitter: @chintansoni202

    View Slide