📜 ⬆️ ⬇️

Dagger 2 for novice Android developers. Dagger 2. Part 1

This article is the fourth part of a series of articles, which, according to the author, are intended for those who cannot understand the implementation of dependencies and the Dagger 2 framework, or are only going to do it. The original was written on December 10, 2017. Free translation.

image

This is the fourth article in the series "Dagger 2 for novice Android developers." . If you have not read the previous, then you here .

Article series



Earlier in the series of articles


In the last article, we realized that a class should not create dependencies. Instead, he should receive them outside.
')
We also looked at a simple example of dependency injection in action. They took the example of the battle of bastards and tried to get rid of hard connections (hard dependencies) through the introduction of dependencies.

How can dependency injection get complicated?


If the project is as simple as the previously reviewed example, then creating instances and manually onCreate() small number of dependencies through the entry point ( main() or onCreate() method) into the program is very reasonable. However, in many projects there are many classes, each of which has different dependencies that must be satisfied. Creating an instance and connecting everything together takes a large amount of code. Even worse, this code will constantly change every time new classes are added to the application and existing classes change to introduce new dependencies.

To illustrate the problem described, let's complicate our example a little. During the war, the battle of bastards ( BattleOfBastards ) will probably need the help of the Allies ( Allies ). Also, the Iron Bank ( IronBank ) will finance the house. The modified main method will be as follows:

 public class BattleOfBastards { public static void main(String[] args){ IronBank bank = new IronBank(); Allies allies = new Allies(bank); Starks starks = new Starks(allies, bank); Boltons boltons = new Boltons(allies, bank); War war = new War(starks, boltons); war.prepare(); war.report(); } } 

Very quickly, the entry point into the application will be filled with a huge amount of code to initialize all dependencies. To create one class with which we will work, you need to initialize several others. As the application grows and new classes are added to it, the entry point into the application will swell and eventually this method will become very difficult to maintain.

Dagger 2 to the rescue


Dagger 2 is one of the open source frameworks for dependency injection (I’ll use DI from Dependency Injection), which generates a large amount of generic code for you. Why is he better than others? Now it is the only DI framework that generates fully traceable Java code that simulates the code that you could write manually. This means that there is no magic in building a dependency graph. Dagger 2 is less dynamic than others (it does not use reflection), but the simplicity and performance of the generated code are at the same level as that of the handwritten code. In short, Dagger 2 generates all the template code for dependency injection for you.

Manual dependency injection management is like dragon glass mining. First, you get permission from the Queen of Dragons, then forge weapons, and only then go to fight with the White Walkers (problems of strong ties). Dagger 2 is like a Valyrian sword - it was created by craftsmen, and all you need is just to use it.

media
image

Understanding Annotation Processors


# Annotations


Annotations are a kind of metadata that can be associated with classes, methods, fields, and even other annotations. Annotations are used in Java to provide additional information as an alternative to XML or marker interfaces (empty interfaces). Annotations can also be accessed during program execution (runtime) through the reflection mechanism.

# Annotation Processors


Annotation handlers are code generators that hide template code from you, creating it for you at compile time. As long as these actions are performed at compile time, there is no negative impact on performance.

# Why should I know about annotation handlers?


Dagger 2 uses them. Thus, it is possible to track all the generated code during compilation. Therefore, there is no performance degradation, and errors are easily tracked.

Examples


You often see the @Override annotation in classes. If you used Butterknife , @BindView is also an annotation that hides behind itself some metadata that helps generate code.

Dagger 2 Annotations


Consider some Dagger 2 annotations before using it. Now @Inject focus on the two - @Inject and @Component .

@Inject


This is the most important annotation. JSR-330 defines this annotation as a label for dependencies that must be provided by the framework for dependency injection.


 public class Starks{ /** *    *  Inject  Dagger **/ //Feild injection @Inject Allies allies; //Constructor injection @Inject public Starks(){ // -  } //Method injection @Inject private void prepareForWar(){ // -  } } 

In other words, the @Inject annotation will tell Dagger which dependencies should be provided to the dependent object. It's like the agents of the iron bank, who negotiate with the houses and determine the amount of credit that they can provide the house.

@Component


This annotation is used for an interface that integrates all parts of the dependency injection process. When using this annotation, we determine from which modules or other components dependencies will be taken. Also here you can determine which dependencies will be visible openly (can be embedded) and where the component can embed objects. @Component , in general, is something like a bridge between @Module (consider this annotation later) and @Inject .

In other words, this annotation is like an agent of an iron bank who is responsible for approving a loan and transferring money to the appropriate account.

Kill the White Walkers with a Valirian sword


Let's use Dagger 2 for an example with the battle of the bastards. In this example, we need two dependencies for the War class - Starks and Boltons .

Dagger 2 Setup


To configure Dagger 2 in IntelliJ Idea, use the build.gradle file from the branch of my project. Also make sure that the annotation processing is enabled (File -> Settings -> Build, execution and deployment -> Compiler -> Annotation Processing -> Enable annotation processing (the flag should be checked)). Do not forget to note this: File -> Settings -> Build, execution and deployment -> Gradle -> Runner -> Delegate IDE build / run to cradle.

Adding @Inject annotation


The plan is to inject the Starks and Boltons into the War class with the help of Dagger 2. What we should explicitly tell him. Below is an example of how to do this using an implementation using a constructor.

 public class Boltons implements House { @Inject //Dagger 2 public Boltons(){ } @Override public void prepareForWar() { // -  System.out.println(this.getClass().getSimpleName()+" prepared for war"); } @Override public void reportForWar() { // -  System.out.println(this.getClass().getSimpleName()+" reporting.."); } } 

 public class Starks implements House { @Inject //Dagger 2 public Starks(){ } @Override public void prepareForWar() { // -  System.out.println(this.getClass().getSimpleName()+" prepared for war"); } @Override public void reportForWar() { // -  System.out.println(this.getClass().getSimpleName()+" reporting.."); } } 

These two dependencies are used in the constructor of the War class, where we must note this.

The plan is to make a dependency or object of class War
available to all other classes. But for the class to work, War needs to provide him with two classes on which he depends - Starks and Boltons .

 public class War { private Starks private Boltons boltons; @Inject public War(Starks starks, Boltons bolton){ this.starks = starks; this.boltons = bolton; } public void prepare(){ starks.prepareForWar(); boltons.prepareForWar(); } public void report(){ starks.reportForWar(); boltons.reportForWar(); } } 

Adding @Component annotation


As we learned earlier, @Component is a bridge between generated code and dependencies. Also @Component tells Dagger 2 how to @Component dependency. Let's make the BattleComponent interface inside the BattleOfBastards class (can be done separately).

 @Component interface BattleComponent { War getWar(); } 

This interface will be implemented by a class that Dagger 2 will generate, and the getWar() function will return an instance of War that we can use in the right place.

Now you need to rebuild the project!

After building the project, you will see that Dagger 2 has generated a class called DaggerBattleComponent - it will help us implement the War class. Use this class to get a copy of War .

 @Component interface BattleComponent { War getWar(); } public class BattleOfBastards { public static void main(String[] args){ //    // Starks starks = new Starks(); // Boltons boltons = new Boltons(); // War war = new War(starks,boltons); // war.prepare(); // war.report(); //  Dagger 2 BattleComponent component = DaggerBattleComponent.create(); War war = component.getWar(); war.prepare(); war.report(); } } 

Using the DaggerBattleComponent class we can use the getWar() method, which returns an instance of War that embeds the Starks and Boltons .

Congratulations! You created the first project using Dagger 2. I really appreciate you taking the time to go so far. Time to celebrate.

Summary


We discussed how using DI manually complicates the work and increases the amount of template code. We further discussed how Dagger 2 helps us get rid of this pain and generates template code itself.

After we have analyzed the information on the annotation processors and the basic annotations of Dagger 2 ( @Inject and @Component ). Then we applied annotations in our example and implemented dependencies using Dagger 2.

What's next?


In the next article, we will work with the classes that Dagger 2 generates and look at the other annotations of this framework. The next article will be released in a week.

Source: https://habr.com/ru/post/344314/


All Articles