Concise explanation of dagger2 usage tutorial

  • 2021-08-31 09:09:53
  • OfStack

Preface

The name of Dagger library not only comes from its original meaning "dagger", but also implies its principle. Jake Wharton pointed out in the introduction of Dagger that Dagger is DAG-er, and DAG here is DAG-directed acyclic graph (Directed Acyclic Graph) in data structure. That is to say, Dagger is a dependency injection library based on directed acyclic graph structure, so there can be no circular dependency in the use of Dagger.

Android development has evolved from the MVC framework that started with 1, to MVP, to MVVM. At present, data-binding of MVVM is still in the experimental stage, and the traditional MVC framework Activity may contain a lot of code, which is difficult to maintain. At present, the mainstream architecture still uses MVP (Model + View + Presenter). However, MVP framework may also concentrate a large number of codes in Presenter. Introducing DI framework Dagger2 can decouple between Presenter and Activity, and between Presenter and other business logic, thus improving modularity and maintainability.

Now the company project used Dagger2, before only a little understanding of 1, not used, and then check the data, organized as follows, easy to get started quickly

4 Basic Notes

1. @ Inject has two main functions, one is to use it on constructors, and let Dagger2 use it by marking constructors (Dagger2 can find this constructor and bring out related instances new when this class instance is needed by Inject marking) to provide dependencies, and the other is to mark variables that need dependencies and let Dagger2 provide dependencies for them.

Field of @ Inject annotation cannot be of private and protected

2. @ Module Classes annotated with Module are specifically designed to provide dependencies. Some people may have some doubts. After reading the @ Inject above, we need to mark the constructor to provide dependency. What if the class constructor we need to provide cannot be modified, such as some classes in jar package, we cannot modify the source code. At this time, you need to use Module. Module can provide dependencies for classes that cannot modify the source code, and of course, Module can also provide dependencies for classes that can be annotated with Inject.

It should be noted here that there are differences between Module and Inject annotations. When @ Inject is used on a constructor, this constructor can have parameters or not. If there are parameters, this Module also needs other Module or @ Inject constructors to provide instances, which is suitable for providing the class itself. However, if @ Module is used, this class annotated by @ Module needs to have a default parameterless constructor (which can be displayed implicitly), otherwise it will report "xxx must be set". If there is no default parameterless constructor, it is necessary to manually pass this instance of Module into Component, which is generally used in MVP mode to provide Activity instance to Presenter instance.

Therefore, if the class only needs to provide itself, it is recommended to use the @ Inject function directly, and if it is used to provide instances of other classes, it is recommended to use @ Module.

3. @ Provides annotates a method with Provides, which can be called when a dependency needs to be provided, so that the pre-provided object is assigned to the variable annotated with @ Inject as a dependency. provides is mainly used to annotate methods in Module.

4. @ Component 1 is generally used to annotate interfaces. Interfaces marked with Component will produce corresponding class instances at compile time to serve as a bridge between providing dependencies and needing dependencies, and inject related dependencies into them.

4 Extended Annotations

1. @ Qulifier There is a concept here, called dependence lost, that is, in the class annotated by Module, there are two Provides that provide instances of a certain class. At this time, Component will not know which instance to use without @ Qulifier annotation. At this time, it is necessary to use @ Qulifier, and the code is provided directly below


@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface A {}

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface B {}

@Module
public class SimpleModule {
 @Provides
 @A
 Cooker provideCookerA(){
 return new Cooker("James","Espresso");
 }

 @Provides
 @B
 Cooker provideCookerB(){
 return new Cooker("Karry","Machiato");
 }
}

public class ComplexMaker implements CoffeeMaker {
 Cooker cookerA;
 Cooker cookerB;
 @Inject
 public ComplexMaker(@A Cooker cookerA,@B Cooker cookerB){
 this.cookerA = cookerA;
 this.cookerB = cookerB;
 }
}

2. @ Named and @ Qulifier1 are similar, and @ Named inherits @ Qulifier and is more convenient to use than @ Qulifier. The sample code is as follows:


@Module
public class MainModule {
 @Provides
 @Named("red")
 public Cloth getRedCloth() {
 Cloth cloth = new Cloth();
 cloth.setColor(" Red ");
 return cloth;
 }

 @Provides
 @Named("blue")
 public Cloth getBlueCloth() {
 Cloth cloth = new Cloth();
 cloth.setColor(" Blue ");
 return cloth;
 }

 @Provides
 public Clothes getClothes(@Named("blue") Cloth cloth){
 return new Clothes(cloth);
 }
}

public class MainActivity extends AppCompatActivity {
 ...
 @Inject
 @Named("red")
 Cloth redCloth;
 @Inject
 @Named("blue")
 Cloth blueCloth;
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 ...
 tv.setText(" I have now " + redCloth + " And " + blueCloth );
 }
}

3. @ Scope local singleton means that there is only one instance of this class in the injected class. What is the local scope, that is, its life cycle scope. Direct code


//PerActivity.java
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface PerActivity {}

//ActivityModule.java
@Module
public class ActivityModule {
 @Provides
 CoffeeShop provideCoffeeShop(){
 return CoffeeShop.getInstance();//1 A common singleton 
 }

 /**
 *  Say the result directly here, @PerActivity Is to use @Scope Annotated, in addition to annotating here, you need to use the Module Class Component Also annotated above the class name of, and then the instance is injected into a class with the same 1 A Component No matter how many fields there are, there will be only 1 Instances. Note: If you use a different Component Instance will still be new CookerFactory Instance, singleton CookerFactory Exist only 1 A Component In the example. So it is called local singleton. 
 */
 @Provides
 @PerActivity
 CookerFactory provideCookerFactory(){
 return new CookerFactory();
 }

 @Provides
 CookerFactoryMulty provideCookerFactoryMulty(){
 return new CookerFactoryMulty();// Non-singleton 
 }
}

//CoffeeShop.java
public class CoffeeShop {
 private static CoffeeShop INSTANCE;
 private CoffeeShop(){
 Log.d("TAG","CoffeeShop New Instance");
 }

 public static CoffeeShop getInstance(){
 if(INSTANCE == null){
 INSTANCE = new CoffeeShop();
 }
 return INSTANCE;
 }
}

//CookerFactory.java
public class CookerFactory {
 public CookerFactory(){
 Log.d("TAG","CookerFactory New Instance");
 }
}

//CookerFactoryMulty.java
public class CookerFactoryMulty {
 public CookerFactoryMulty(){
 Log.d("TAG","CookerFactoryMulty New Instance");
 }
}

// Except in Module Adj. Provides Write in the method @Scope Also need to be in Component Above the class name, write the custom here @Scope The name is PerActivity
@PerActivity
@Component(modules = {ActivityModule.class})
public interface ActivityComponent {
 void inject(MainActivity simpleActivity);
}

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface B {}
0

Running result


@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface B {}
1

@ Singleton This annotation inherits @ Scope, and the difference when using it is that you don't need to customize @ Scope. For example, the step of defining @ PerActivity above is unnecessary. Other usage is the same as using @ PerActivity1 model 1, and annotations are written on the class name of Component and the Provides method of Module.

Note: Again, local singletons are valid only if the same Component instance provides dependencies, and different Component instances can only implement singletons through Component dependencies. That is, although you add an PerActivity annotation or an Singleton annotation to both Component interfaces, the two Component provide dependencies that are not related, and they can only implement singletons within their own scope
Use local singletons on the constructor of the @ Inject annotation to declare scope directly on the class name (add @ Singleton or custom Scope to the class name)

Dependence: dependencies

When Component depends on Component, the @ Scope of two Component cannot be the same, otherwise it will compile errors. I am not very clear why it is designed like this. Please tell me thank you if you know it.

The sample code for dependencies is as follows:


@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface B {}
2

How does daggar2 choose dependencies, in this order

When Component calls the inject method, it searches for the fields annotated with @ Inject in the injected class, and then looks in the Component for @ Component (modules =. . . ) Module registered in the annotation, If you search for an Module method with an @ Provides annotation that provides the required instance of the field of the @ Inject annotation, Call the corresponding method to complete the injection, otherwise find all the classes with @ Inject annotation constructor, if find, call the corresponding constructor to complete the injection, if you need to obtain the instance of parameters when you get the instance, then inject the parameter instances in turn according to the flow just now

Draw a simple flow, as follows

Component.inject- > Search for Module in Component- > Found provides an instance of the method that calls the @ Provides annotation

​ - > Search the constructor of @ Inject annotation if it is not found

​ - > If you can't find it, report an error. . .

If you can't find it, you must report it wrong. . . However, it will give priority to looking for Module registered by Component, and the constructor registered by @ Inject can call inject method of any Component to complete injection, because the constructor registered by @ Inject does not need to be registered in Component, which is different from Module. Module needs to be registered in an Component, while @ Inject does not need to

For an example of using Dagger2 in MVP, I won't post the code. I'll just look at the following link: https://www.ofstack.com/article/138093. htm

Summarize


Related articles: