📜 ⬆️ ⬇️

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

This article is the fifth part of a series of articles that, 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 17, 2017. Free translation.

image

This is the fifth 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 previous article, we discussed how manual use of dependency injection (DI) complicates the work and increases the number of template code. After we looked at how Dagger 2 saves us from this pain and generates sample code for us. We also ran through the annotation processors and Dagger 2 basic annotations. Then we applied these annotations using an example and implemented dependencies using Dagger 2.
')

Anatomy of a DaggerBattleComponent


For a better understanding of Dagger 2, consider the class DaggerBattleComponent . Place the cursor on DaggerBattleComponent and press Ctrl + B (or Ctrl + LMB, Command + LMB). You will see the following:

 @Generated( value = "dagger.internal.codegen.ComponentProcessor", comments = "https://google.imtqy.com/dagger" ) public final class DaggerBattleComponent implements BattleComponent { private DaggerBattleComponent(Builder builder) {} public static Builder builder() { return new Builder(); } public static BattleComponent create() { return new Builder().build(); } //    @Override public War getWar() { return new War(new Starks(), new Boltons()); } public static final class Builder { private Builder() {} public BattleComponent build() { return new DaggerBattleComponent(this); } } } 

This is what Dagger 2 generates for us to solve the problem of hard-dependency . If you look at the interface that implements the class, you will see that this is the BattleComponent - the interface that we previously created and described in it the getWar() method to provide an instance of the class War .

This dependency is provided using the builder pattern. About this template is more fashionable to read here and here .

Learn something new


I hope that you clearly understand why you need the getWar() method. Now I want to add a couple more dependencies: Starks and Boltons . Add methods to the interface:

 @Component interface BattleComponent { War getWar(); //   Starks getStarks(); Boltons getBoltons(); } 

After making changes, reassemble the project. Now check the DaggerBattleComponent class. If you did everything right, you will see the following.

 @Generated( value = "dagger.internal.codegen.ComponentProcessor", comments = "https://google.imtqy.com/dagger" ) public final class DaggerBattleComponent implements BattleComponent { private DaggerBattleComponent(Builder builder) {} public static Builder builder() { return new Builder(); } public static BattleComponent create() { return new Builder().build(); } @Override public War getWar() { return new War(getStarks(), getBoltons()); } @Override public Starks getStarks() { return new Starks(); } @Override public Boltons getBoltons() { return new Boltons(); } public static final class Builder { private Builder() {} public BattleComponent build() { return new DaggerBattleComponent(this); } } } 

As you can see, Dagger 2 implemented the getStarks() and getBoltons() methods.

We told Dagger 2 to get these dependencies using the @Inject annotation in the Boltons class. Let's break something. Remove the @Inject annotation from the Boltons class. Build the project again.

Nothing has happened? Yes, you have not received any errors, but try running the project. You should receive the following error:

image

If you read the text of the error, it clearly indicates that the getWar() and getBoltons() methods will not work if there are no annotations with @Inject or @Provides .

As previously mentioned, Dagger 2 makes it easy to track errors. You can play a little with this class.

Annotations @Module and @Provides


Digging deeper and sort out a couple of useful annotations - @Module and @Provides . They should be used if the size of your project increases.

@Module


In short, this annotation marks the modules and classes. Let's talk about Android. We can have a ContextModule module and this module will provide ApplicationContext and Context dependencies for other classes. To do this, we need to mark the ContextModule class with the @Module annotation.

@Provides


In short, this annotation is needed to mark methods that provide dependencies inside modules. In the previously described example, we marked the ContextModule class with the @Module annotation, but we also need to label the methods that provide the ApplicationContext and Context dependencies with the @Provides annotation.

See a small example ( link to a branch ).

Example


Take the two services provided by Braavos - money (Cash) and soldiers (Soldiers) (I'm not sure that they provide such services, but consider this for example only). Create two classes:

 public class Cash { public Cash(){ // -  } } 

 public class Soldiers { public Soldiers(){ // -  } } 

Now create a module and name it BraavosModule . He will supply us with two dependencies - Cash and Soldiers .

 @Module //  public class BraavosModule { Cash cash; Soldiers soldiers; public BraavosModule(Cash cash, Soldiers soldiers){ this.cash=cash; this.soldiers=soldiers; } @Provides //   Cash Cash provideCash(){ return cash; } @Provides //   Soldiers Soldiers provideSoldiers(){ return soldiers; } } 

As we saw earlier, it is necessary to mark all modules with the @Module annotation, and methods that provide dependencies with the @Provides annotation.

Let's go back to the BattleOfBastards class and let the component implement the provideCash() and provideSoldiers() methods.

 @Component(modules = BraavosModule.class) interface BattleComponent { War getWar(); Cash getCash(); Soldiers getSoldiers(); } 

 public class BattleOfBastards { public static void main(String[] args){ Cash cash = new Cash(); Soldiers soldiers = new Soldiers(); BattleComponent component = DaggerBattleComponent .builder() .braavosModule(new BraavosModule(cash, soldiers)) .build(); War war = component.getWar(); war.prepare(); war.report(); //     component.getCash(); component.getSoldiers(); } } 

Notice that the module has been added to the annotation @Component . This suggests that the component will contain this module within itself.

@Component(modules = BraavosModule.class)

After all changes, build the project again. You will see an error in the .create() method of the .create() class. It arose due to the fact that when you add a module, you must pass this dependency to Dagger 2. It looks like this:

BattleComponent component = DaggerBattleComponent.builder().braavosModule(new BraavosModule(cash, soldiers)).build();

After turning on all modules, you can start using their methods through the Component .

component.getCash(); component.getSoldiers();

If you want to make sure, hover over DaggerBattleComponent and press Ctrl + B (or Ctrl + LMB, Command + LMB). You will see that the BraavosModule module BraavosModule included in the class to provide the Cash and Soldiers dependencies.

Summary


We analyzed the classes generated by Dagger 2 and noticed that Dagger 2 uses the builder pattern to provide dependencies. We also looked at a simple example of using the @Module and @Provides .

What's next?


In the next article, we will look at an example of an Android application using Dagger 2.
The next article will be released on December 29th or later. Thank you for your patience.

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


All Articles