Dagger 2. Treating addiction using Google


    Posted by Konstantin Mars
    Senior Developer @ DataArt,
    Co-Organizer @ GDG Dnipro


    Dependency injection



    What, why and when is it needed


    Today we’ll talk about a tool that helps improve the quality of development for Android. This problem can be solved using Dependency Injection (DI). Usually this term is associated with injections, syringes and a little with “addictions”. In fact, Dependency Injection is a design pattern that implements the principle of dependency inversion and implements the rules for creating objects and the independence of implementations.
    So, we have a class, the class has a constructor, and there are several members of the class. When you create the essence of this class, you need to provide the class with instances of the very types that are declared for its members. In this case, this is the name of the machine and the type of engine Engine. You will use links to objects; accordingly, links inside your class will not be empty.
    Thus, you implement OOP and can create objects.

    Creating classes begets ...





    • Composition is not inheritance.
    • Links will not be empty. 


    The ability to create objects ...


    You can create an object, set the name of the machine and create some new engine.



    You can create different objects, for example, create a different type of engine or just another engine.



    Suppose you can create two different objects that you will use. In this case, the same engine from the Patriot. Accordingly, if you put this engine in the Jeep Grand Cheerokee - it will be a little strange. But, nevertheless, you can do it. In this case, the so-called “composition” pattern is used when the entities that you create will be included in another entity, and this, as you see, will not be inheritance, but rather composition.



    Everything is very simple here: if you look at SuperTunedEngine, you will understand that in fact it is an inheritor of a certain type, which has already been declared in advance, and also, possibly, is an implementation of the interface - for us this is unprincipled. In this case, Engine may be an interface.



    Looking at two ads, you see: we can make two objects depend on some other object. Actually, this is a dependency. Thus, the ability to create objects creates dependencies - a pretty commonplace thing.

    And ... addictions 


    Car depends on Engine. Engines may vary. We'll probably need different engines for testing and production.



    As you can see in the diagram (the image is not from our example), the dependencies are very different. You will have dependent services, dependent activity, presenters, view, controllers. All these entities are intertwined by dependencies. If you try to express it graphically, you get about what you see now in the picture.
    There will be much more dependencies in a real working system. Tests conducted by well-known companies that provide tools for testing Android applications show that there are thousands of dependencies, even in simple, at first glance, applications. On average, thousands and tens of thousands of dependencies are found even in the simplest applications. To implement these dependencies as efficiently as possible, without instantiating any other classes inside your class and without adding a bunch of code that will repeat and add you extra work, there is a Dagger tool.

    Dagger and JSR-330 Standart



    Abstract Inject


    Dagger is based on the JSR-330 standard. Google has been using this standard for a very long time, and it is the standard for Java Injection.

    Little else NOT history 


    • Dagger 2 - Google, Greg Kick
    • Dagger - Square, Jake Wharthon
    • Guice - Google, Jesse Wilson 

    Let's look at a little history: Google once created a product like Guice (popularly called it "Juice", and in our latitudes - "Goose"). Guice worked with reflection, he followed annotations, but subsequently the developers from Square improved the system that was in Guice and created Dagger1. 
    Dagger1 was a cool tool, but, as practice shows, here you can improve something. By the way, Dagger1 also used reflection. And in 2015, Google developers released Dagger2. It seems that more recently, Jake Wharton (a well-known developer from the company Square) announced its release with an eye on the fall - the promise has been fulfilled, we have a quality product that is ahead of the competition based on test results.

    Inversion of Control ( Engl.  Inversion of Control, IoC)





    Back to the standards and terminology. So, we have a product that appeared during evolution. It uses JSR-330, which provides a number of annotations. In addition, it follows certain principles, a kind of development pattern, one of which is Inversion of control (IoC). 
    The process of providing external dependency to a software component is a specific form of “inversion of control” (IoC) when it is applied to dependency management. In accordance with the principle of single responsibility, the object gives care about building the dependencies it needs to an external, specially designed for this general mechanism.
    This thing is related to architectural patterns. We must write the application in such a way that the inner classes associated with the domain logic are independent of the outer classes, so that the application is written based on the interfaces. Thus, the distinction of the zone of responsibility is realized. Turning to some kind of implementation, we turn, first of all, to the interface . Inversion of Control is implemented through Dependency Injection. The toolkit itself is called Dependency Injection (DI). 

    Reflection vs Compile time


    • Dagger2 vs Dagger1


    Dagger2 uses code generation, unlike Dagger1, which used reflection.

    JSR-330



    JSR-330 aka javax.inject


    • Inject , Qualifier , Scope . etc.
    • Standardized Dependency Injection API
    • Reference Implementation: Google Guice 2.0
    • Also supported by Spring since 3.0
    • Defines API, not injector implementation or configuration

    JSR not only describes Inject annotation , but also provides a whole package of annotations that will allow you to declare how entities will interact to provide Dependency Injection.
    For example, I talk about a specific family of Dependency Injection products that follow this standard. There are other products that do not follow this standard, we won’t talk about them today, but they exist. There is Inject , Qualifier , Scope - we will talk about them later. These annotations were not created only for Dagger2, they exist for other injectors, for example, Guice.

    So, it's time to add some magic to our code ...





    We start by annotating class members with the annotation inject . Everything is quite simple. In order to instantiate these dependencies in the future, our Dependency Injection toolkit was able to correctly choose where to instantiate and what, we must also annotate the constructor. Here the situation becomes a little more interesting.

    Pay attention to the default constructor





    The easiest way to provide injection is to create a default constructor. Then annotate the default constructor itself and those members that require instantiation of this class with an injection. This is done very simply.

    Constructor with parameters - a good place for modifications





    In real life, we will need constructors with parameters. The system will be able to pick up some of them automatically if they have default constructors. And some, for example, the same Engine, may have to be constructed manually. 
    You will also inject presenters using such constructors; this is very often used in MVP (Model-View-Presenter).

    And yet - how to make it work?





    Dagger2.0 injection structure


    An injection framework is the interconnection of Dagger components that allows us to combine inject annotations and combine class declarations. 

    Components and Modules




    Pic. author - Miroslaw Stanek from Azimo
    http://frogermcs.github.io/dagger-graph-creation-performance/
    The structure of Dagger injection includes components and modules. If you look at the picture (from an article by Miroslav Stanek from Azima), you will see that the components are containers for the modules, and there may be others inside one component. Later we will see that the components that are nested are called subcomponents (@ SubСomponent). And we cannot call them simply “components” according to the rules of injection.

    Module - Generator Collection





    Abstract Module - an annotation that says that this entity - this class - is a module that will generate instances of objects.
    Here, too, everything is quite simple. The annotations that generate is the provides annotation. The provides annotation simply indicates that this module method will supply you with an entity. The most interesting will happen inside this method. 
    You will need, following these rules, to somehow instantiate the object. Some objects will depend on each other. Some objects will depend on the members of the module class, which you can store in the module. For example, the same context you can put in a module. The module remembers it and then, when instantiating the same prezenters, you will generate new presenter entities based on the context that the module remembered once when it was created.
    As you can see, the module has a constructor. In this case, instead of context, we pass Application. When creating something new, we can return what is stored in the module itself.

    What is singleton? 


    When creating some entities, we set certain parameters. Singleton (English  Singleton ) - the instruction which says that where the injector will find the annotation inject , he should not instantiate a new object, but should reuse the singleton object already instantiated already once. 

    @Component


    Component - host for modules, injector for classes, the root of the dependency tree.



    With the component, everything is a little more interesting. The component must take into account the lifetime of the modules that it includes. If we try to use a singleton for a component that uses instantiation lifetimes, conflicts will arise. Therefore, you need to clearly understand that, for example, the component for Application will be a singleton, because the object of the Application class exists in a single instance, and exists throughout the life of the application. For activity, for example, it can also be a singleton, and its lifetime will be tied to the lifetime of the activity. If necessary, it is possible to annotate a component with an additional Singleton annotation . There is a list of modules that includes this component.
    For example, in Activity there will be an inject annotation. The component must specify modules that do provides this activity. You must indicate in the component where we inject. That is, we must specify a specific class, and note that, for example, you cannot write BaseActivity as a base class, because then the injection will occur only in Base aActivity, and in MainActivity, where you need, for example, to inject some kind of presenter, the rules will be a little different.



    The inject method is a description of who is dependent. Modules - A description of those who provide dependencies.
    Let's get back to the module. A module is declared as a class. It is important to note that a module is a real class that has real references to real objects. And you create it manually when declaring a component, when bundling. The component, in turn, is the object that Dagger generates. Just at this moment, the magic of code generation happens. Therefore, a component is declared as an interface. 

    Initializing the generated code used component





    For example, DaggerAppComponent is initialized inside our application. Please note that generated code used (component initialization) means that we use the generated code. For example, DaggerAppComponent can be detected. As you saw earlier, there was no Dagger prefix. Where did he come from? Yes, Dagger generated the code. He generates it fast enough. If you accidentally break the injection that we are talking about (about its structure), you will end up with a DaggerAppComponent. If you make a small mistake and incorrectly specify the class, the generation will not work - DaggerAppComponent will not appear, and all the magic that provides us with binding our activity and other classes will not work without the generated class. Because the component is the root of the whole tree - this is the foundation. And without it, everything else does not work.
    It should also be noted that the component has a builder. Builder - design pattern. We understand that the builder has some arguments that determine how our component will be built further, for example, the AppModule method - an automatically generated method that takes the AppModule instance class as an argument. We create the module with our hands and set the parameters for it. And we call the build method to get the AppComponent. This link has an example from real code: http://github.com/c-mars/Dagger2Scopes.git .

    Inject This! :)





    Puttin 'magic will work only after injection ... :)


    The Application class has methods that provide access to it. Moreover, this is not a static method, you can just get the context from get application. You can add it to your class - it will turn out the same and there will be no magic here. But, which is really important for us, we will have this getAppComponent.
    The idea is that Application stores the AppComponent. We call some additional methods on this component, and then apply the inject method. As you noticed, this is an injection indicating the specific class that we declared in the component. In this case, it is the LoginActivity class. You see the injection in the annotation, see how we injected the dependencies. Magic will only work after injection.

    Custom Scopes and Efficient Memory Management


    Custom Scopes, as such, serve to provide your instantiated classes with a specific lifetime.

    Object Life Cycle




    Pic. author - Miroslaw Stanek from Azimo
    http://frogermcs.github.io/dagger-graph-creation-performance 
    For example, you have activity, and they live for a short time, go from one screen to another and kill everything. That is, everything that is injected into activity can be cleaned after that, and the application will consume less memory. Some class of user data, for example, User, will live between logins. Application Scope is the most important, root scope, which lives the longest.

    And again, the same nesting doll.
    The component has a scope




    Pic. author - Miroslaw Stanek from Azimo
    http://frogermcs.github.io/dagger-graph-creation-performance/

    This mysterious 'plus' ...





    Now let's pay attention to the plus.

    Subcomponent declaration





    Scope annotation allows you to generate scopes of a specific lifetime. For example, an ActivityScope will live as long as an activity lives. They annotate components as subcomponents.

    But there was a module!





    The data inside is described in the same way as in the root component. Except for one thing: when you call plus, pass the module there and instantiate it, you will get the subcomponent that you need.

    Adding a subcomponent to the root of the dependency tree





    What is it used for? So that scopes, which can be declared as interfaces, limit the lifetime of our objects. 

    Abstract Scope







    This kind of Scope will limit the lifetime statically, depending on where you are injected. And the other will limit dynamically.
    Dynamic means that you will manage it manually, and everything will be deleted using the garbage collector ("garbage collector"). 
    We annotate the component needed by the scopes.

    @ActivityScope







    @UserScope, for example, how it will work this same scope that has @Retention (RUNTIME). It can be controlled manually.  

    @UserScope





    To manage it manually, you save the link to the component inside the application next to the AppComponent.



    It is created using a special method, an example of code that you can see. Then the code was cleaned, sent to the release, and the garbage collector will delete it. When does this happen? When the user logged out ( "logged in" ). The next time you call another createUserComponent, this component will be created again with other user data.

    Finally ... what to inject?


    • Demo data modules.
    • Presenters.
    • Singleton.
    • Test implementations of classes.
    • ... Everything else that instantiates and creates dependencies.

    In fact, you need to inject something that will help you inject memory more efficiently and write code. Presenters should definitely be used. 
    Singletons are convenient. In the example that I will give you, we injected Mock data for the demo version, and they could also be used with variations during testing. 

    Home readings


    Sample code: http://github.com/c-mars/Dagger2Scopes.git



    I recommend reading Fernando Cejas about design patterns. Miroslav Stanek very well described scopes. He has a wonderful article on how to properly manage @Retention (RUNTIME) and clean memory in time. And of course, visit the official Dagger2 page.

    The meaning of the code


    How we organized fast Agile development using Mock modules and eventually overtook the server side.
    The story is like that. We used Dagger 2 in the unit test project, with the correct MVP separation, but the key point was that there was no server side at that time. And we wrote an application that should take data from the server, show it, process it all beautifully, by analyzing the data. The main task was that with the advent of REST services we could quickly switch to them. You can do this quickly, only by changing the code manually. If there were modules and components, after the server appeared, we easily replaced the Mock data (which was supplied by injection) with real data with the REST API, replacing only one module and one line of code in the declaration of the root component.

    Also popular now: