preface
It is said on the Internet that Dagger2 is quite difficult to get started. I have read a lot of materials and encountered a lot of unknown or vague knowledge points when using Dagger2. Moreover, most of the blog materials are quite old. Suddenly for a moment, I realized why, so I summarized four articles. It’s still a bit cumbersome to use in Java, so don’t be afraid to get your hands dirty and apply it to our Android project.
This tutorial is divided into four parts: 1, Basic Knowledge of Dagger2 and using it in Java (1) 2, basic knowledge of Dagger2 and using it in Java (2) 3, Advanced knowledge of Dagger2 and using it in Android 4, Dagger2 is used in MVP framework
Just to be clear, the benefits of Dagger2 are not the focus of this article. You can do it yourself. Dagger2 is a dependency annotation framework, as is butterknife between us, and such framework dependencies are usually 2 lines long. The second line begins with annotationProcessor. This is actually apt’s tool, and this dependency annotation framework does not affect performance (not reflection), apt generates the code to be used at compile time. @Component is equivalent to a syringe (remember interface); @Module is equivalent to injection, which is the data source (remember this is a class or abstract class), at which point the injection should be placed in the specified syringe such as @Component(modules =…). ; Inject is equivalent to annotating the injected body.
After the explanation is to go through the simple process, realize the function, and then talk about the general understanding. The code posted on the blog may omit part of the code for easy understanding. The Demo and notes on Github are very detailed and close to perfect 0-0# If you have no idea about it at all, I recommend that there are 3 introductions about the meaning of Dagger and how it works
First add dependencies
implementation 'com. Google. Dagger: a dagger: 2.24'
annotationProcessor Com. Google. "dagger: a dagger - compiler: 2.24"
Copy the code
1. Simple use of @inject & @Component (without @Module)
First, define an arbitrary class: Person. The non-parameter constructor is annotated with @Inject:
public class Person {
@Inject
public Person(a) {}}Copy the code
Component
@Component
public interface AstudyActivityComponent {
void injectTo(AstudyActivity astudyActivity);
}
Copy the code
After completing the above steps, click Make Project under the Build TAB in Studio. Let apt generate the code for us. The general generated code is Dagger+ you define the Component class name. After this step is not repeated, you must Make apt generate code, Make Project and then in our Activity:
public class AstudyActivity extends BaseActivity {
@Inject
Person person;
@Override
// Here is my encapsulation of onCreate, omit some code, just for understanding, please ignore later!
protected void processLogic(a) {
/ / the first
DaggerAstudyActivityComponent.create().injectTo(this);
/ / the second
//DaggerAstudyActivityComponent.builder().build().injectTo(this);}}Copy the code
Build our Component in our Activity and register it in our Activity to use our Person as our @inject. Initialization here has two kinds: 1, DaggerAstudyActivityComponent. The create () injectTo (this); 2, DaggerAstudyActivityComponent. Builder (). The build () injectTo (this); This must be used to pass values using module
Then a simple implementation is achieved, ignoring the new procedure, from which its decoupled implementation is known.
Simple use of general steps (please skip to understand) :
- Step 1: Annotate it with Inject to tell Dagger2 that it can instantiate this class, such as Person
- Step 2: Use the Component annotation to inject the dependency into the AstudyActivity
- Step 3: Use Make Project under Android Studio Build to generate the code that will automatically generate the DaggerComponent class named Dagge+ the name of the Component we defined
2. Use with @Module
Why is there a module concept? For example, the constructor of Person can be annotated with @inject, but there is no way to add it to the imported third party library. Therefore, @Module is used to solve this problem. Here we define Human and pretend it is a third party library that doesn’t use @inject
public class Human {
public Human(a) {}}Copy the code
The next step is to define our data source Module, which is where we will initialize it. The Person constructor was @Inject. First, the best naming rule is to add Module, with @Module notation. And then I’m going to define a method that I call @Provides. The return value is the class we need to initialize, preferably with a method name ending in Provides. There are actually multiple methods that can be defined here, I’ll say later
@Module
public class BstudyActivityModule {
@Provides
Human humanProvides(a){
return newHuman(); }}Copy the code
Then there’s our Component. Here is different from before (modules = BstudyActivityModule. Class), which is equivalent to the injection into the syringe. There can be more than one Module, as described later
@Component(modules = BstudyActivityModule.class)
public interface BstudyActivityComponent {
void injectTo(BstudyActivity bstudyActivity);
}
Copy the code
After Make Project, the operation in the Activity is exactly the same as before.
Use general steps with Module (please skip to understand)
- 1. Assuming that Human cannot be changed at will and there is no @inject annotation (third-party class library, not your project code certainly does not have @Inject), annotate BstudyActivityModule with @Module. The return value of the @provides annotation method is the type of Inject we need
- Annotate the Component interface with @Component and link to the Module class from the first step using modules=.
- 3. Then use it the same way as in AstudyActivity
3. Pass parameters through Module
That’s not really important, but that brings us to 4 and 5. Understand this step, will be easier to understand later. First we assume two classes, female human, and soul: and the soul class has the property of money. The soul class is the attribute of the woman. The soul classes are as follows:
public class Soul {
private int money;
public Soul(a) {}public int getMoney(a) {
return money;
}
public void setMoney(int money) {
this.money = money; }}Copy the code
The women are as follows:
public class Woman {
private Soul soul;
public Soul getSoul(a) {
return soul;
}
public Woman(Soul soul) {
this.soul = soul; }}Copy the code
Let’s define our Module first. Since you can pass, of course, there is a money attribute. Finally we rely on annotations to use the Woman class. Our providesWoman method is labeled with @provides, at which point it goes back to find the initialization of Soul, first via @provides. That’s when providesSoul was found. Thus the female human was formed. Suppose there is no providesSoul at this time. He’ll look for constructors in the Soul class that aren’t annotated with @Inject. If not, sorry. error
@Module
public class CstudyModule {
private int money;
public CstudyModule(int money) {
this.money = money;
}
@Provides
Soul providesSoul(a) {
Soul soul = new Soul();
soul.setMoney(this.money);
return soul;
}
@Provides
Woman providesWoman(Soul soul) {
return newWoman(soul); }}Copy the code
Next comes the Component, unchanged
@Component(modules = CstudyModule.class)
public interface CstudyActivityComponent {
void injectTo(CstudyActivity cstudyActivity);
}
Copy the code
The Activity has changed slightly, of course, to pass the parameter. We passed $100 to a woman’s soul, and yes, a woman is only worth $100!
public class CstudyActivity extends BaseActivity {
@Inject
Woman woman;
@Override
protected void processLogic(a) {
DaggerCstudyActivityComponent.builder()
.cstudyModule(new CstudyModule(100))
.build().injectTo(this); }}Copy the code
Attention points (please skip to understand) :
- When the Module constructor takes arguments and the arguments are used, the produced Component class does not have a create() method.
- The module here does not have the providesSoul() method, and there is another case where @inject is also possible as long as the Soul constructor has it.
@component. Builder (@component. Builder)
We put the 3, through the Module and apt opens at the generated code DaggerCstudyActivityComponent; Look at the picture below and see if you can find a Builder class, which is automatically generated by APT for us. Of course, we can also implement it ourselves
3. Pass parameters through Module
- Method 1: return the value Builder method, here if the module we pass will be the main, otherwise it will help us to generate a module with money 0. Of course, you can also pass arbitrary data types, but it doesn’t work. You can try it.
- Method 2: a method that returns the value of the current Component. The name of the method can be customized
@Component(modules = CstudyModule.class)
public interface DstudyActivityComponent {
void injectTo(DstudyActivity dstudyActivity);
@Component.Builder
interface Builder {
Builder cstudyModule(CstudyModule cstudyModule);
DstudyActivityComponent build(a); }}Copy the code
The same is true for activities. Only we automatically help us generate the system, to write it. Post the Activity code instead
public class DstudyActivity extends BaseActivity {
@Inject
Woman dWoman;
@Override
protected void processLogic(a) {
DaggerDstudyActivityComponent.builder()
.cstudyModule(new CstudyModule(100))
.build().injectTo(this); }}Copy the code
Generally understood and summarized as (please skip to understand) :
- Builder cstudyModule(cstudyModule cstudyModule){} This is dagger2 automatically generated (you can also pass, app/build/generated/source/apt/debug/your package name/DaggerAppComponent. Java directory to find)
So the use of @ component. Builder, using the example of module passing arguments. You don’t need to change anything else. You need to change Component. Define a Builder and tag it with @Component.Builder. Here are two methods:
- Method 1: return the value Builder method, here if the module we pass will be the main, otherwise it will help us to generate a module with money 0. Of course, you can also pass arbitrary data types, but it doesn’t work. You can try
- Method 2: a method that returns the value of the current Component. The name of the method can be customized
4. Use @Component.Builder.
We always need new CstudyModule(100). It was originally said that Dagger2 omitted the new process when used, decoupled. But it’s new. It’s low. No rush, the mighty Google has it all figured out. A new annotation @bindsinstance is encountered. @bindsinstance: write class constructor, assign class constructor, change Module first. We remove the Module constructor and the money member variable property and add money to providesSoul as a parameter. @bindsInstance looks for the parameter of the @provides method and initializes it if the type is the same
@Module
public class EstudyModule {
@Provides
Soul providesSoul(int money) {
Soul soul = new Soul();
soul.setMoney(money);
return soul;
}
@Provides
Woman providesWoman(Soul soul) {
return newWoman(soul); }}Copy the code
Module = eStudyModule.class. I’m going to ignore that, and I’m going to go straight to the key points because you know what I’m doing. Annotate our method that returns Builder with @bindsinstance. I’m going to change the argument to int Money. Of course you can change it to the @Provides notation, but if you change it to Soul Soul, of course you’re still going to initialize it with new Soul. The process is the process
@Component(modules = EstudyModule.class)
public interface EstudyActivityComponent {
void injectTo(EstudyActivity estudyActivity);
@Component.Builder
interface Builder {
@BindsInstance
Builder initMoney(int money);
EstudyActivityComponent build(a); }}Copy the code
Finally, our Activity
public class EstudyActivity extends BaseActivity {
@Inject
Woman woman;
@Override
protected void processLogic(a) {
DaggerEstudyActivityComponent.builder()
.initMoney(100)
.build().injectTo(this); }}Copy the code
If you look at this, Dagger2 is still interesting. It gets more interesting later. Of course it’s getting messy, but you need to get excited. It’s the essence.
It depends on the dependence of Component
Here we use activities and fragments as examples. Let’s say we inject the Human class into our Activity dependency and use the Module in our Fragment
@Module
public class FstudyActivityModule {
@Provides
Human providesHuman(a) {
return newHuman(); }}Copy the code
Void inject(FstudyActivity FstudyActivity). It is important that we return the dependency injection class and define the method provideHuman, because Component depends on Component. So it makes sense
@Component(modules = FstudyActivityModule.class)
public interface FstudyActivityComponent {
Human provideHuman(a);
}
Copy the code
Use dependencies to build FragmentComponent as a child Component, because there are children. Write down our parent Component dependencies = FstudyActivityComponent. Class. After that is injected into the Fragment
@Component(dependencies = FstudyActivityComponent.class)
public interface TestFragmentComponent {
void inject(TestFragment testFragment);
}
Copy the code
In an Activity, make it an ActivityComponent and provide a method that feeds the parent Component to the Fragment
public class FstudyActivity extends BaseActivity {
private FstudyActivityComponent fstudyActivityComponent;
@Override
protected void processLogic(a) {
fstudyActivityComponent = DaggerFstudyActivityComponent.create();
}
public FstudyActivityComponent getFstudyActivityComponent(a) {
returnfstudyActivityComponent; }}Copy the code
In the Fragment
public class TestFragment extends BaseFragment {
@Inject
Human human;
@Override
protected void processLogic(Bundle savedInstanceState) {
FstudyActivityComponent fstudyActivityComponent = ((FstudyActivity) getActivity()).getFstudyActivityComponent();
DaggerTestFragmentComponent.builder()
.fstudyActivityComponent(fstudyActivityComponent)
.build().inject(this); }}Copy the code
Now you can use human in fragment. In Java, it is really around, the code is too much for you to accept. It is recommended to understand first, and the following article is used in Android, you will be very cool.
Generally understood and summarized as (please skip to understand) :
- FstudyActivityModule = FstudyActivityModule = FstudyActivityModule = FstudyActivityModule
- FstudyActivityComponent returns the class to be injected
- 3. On the TestFragment side, create a TestFragmentComponent that relies on FstudyActivityComponent.
- 4. Define a method in FstudyActivity that provides the FstudyActivityComponent for TestFragment to use
- 5, In TestFragment, register under OK. Very round, personal advice first understand this process is good
7. Component relies on Component and uses @subComponent.
It’s the same effect, but in a different way, to show you more about Dagger2. Same with the above example. The Module is the same as above (I’m doing this for the sake of regionalize the Demo). First build a child Component, FragmenComponent, annotated with @subComponent, and inject it into our Fragment. Why build the child Component first. Because the child Component returns in the parent Component.
@Subcomponent
public interface DemoFragmentComponent {
void inject(DemoFragment demoFragment);
}
Copy the code
Then the parent Component, ActivityComponent, father Component everything is normal, the return value is the child Component
@Component(modules = GstudyActivityModule.class)
public interface GstudyActivityComponent {
DemoFragmentComponent demoFragmentComponent(a);
}
Copy the code
Initialize our parent Component and provide methods that return the parent Component for use by the Fragment. And then the Fragment
public class DemoFragment extends BaseFragment {
@Inject
Human human;
@Override
protected void processLogic(Bundle savedInstanceState) {
GstudyActivityComponent gstudyActivityComponent = ((GstudyActivity) getActivity()).getGstudyActivityComponent();
gstudyActivityComponent
.demoFragmentComponent()
.inject(this); }}Copy the code
Now you can use human in Fragment. Okay, so title 6, title 7 works the same way.
Generally understood and summarized as (please skip to understand) :
- Create a Component subclass, annotated with @SubComponent, DemoFragmentComponent
- Create a parent Component: GstudyActivityComponent, define a method to return the subclass Component.
- Define a method in GstudyActivity that provides the GstudyActivityComponent for DemoFragment to use
- 4. Register with DemoFragment and you’ll be fine. Similar to dependencies
- Note that the parent Component is passed through the child Component. Instead, get the child Component from the parent Component and inject it directly
8, Component depends on Component, use @subComponent. Builder (same as headings 6 & 7)
The effect is the same, the way is different, the purpose is still more understanding Dagger2. You can see that the annotation here is @subComponent.Builder. So it’s similar to using @Subcomponent.
Well, let’s take the example above. We need to change the parent Component and child Componet. At this point we can’t help but think about @component. Builder. Is it the same? At this point I can only say similar, but different. After all, there’s a sub here. Let’s take a look at @component. Builder and copy it.
@Component(modules = CstudyModule.class。)
public interface DstudyActivityComponent {
void injectTo(DstudyActivity dstudyActivity);
@Component.Builder
interface Builder {
Builder cstudyModule(CstudyModule cstudyModule);
DstudyActivityComponent build(a); }}Copy the code
Let’s find a way to write @subComponent.Builder. @subComponent.Builder must be used under @subComponent. Modules = cstudyModule.class Replaced by @Subcomponent. Without Module we use no arguments
@Subcomponent
public interface OtherFragmentComponent {
void inject(OtherFragment otherFragment);
@Subcomponent.Builder
interface Builder {
Builder noModule(a);
OtherFragmentComponent build(a); }}Copy the code
I’m going to tell you that the results of the run were wrong.
@Subcomponent.Builder types must have exactly one zero-arg method, and that method must return the @Subcomponent type. Already found: hstudyActivityModule()
We don’t need the Build method. We Already found: hstudyActivityModule(). Sub: Component inherits its parent Componet. And Dagger2 is already default internally, so there is no Builder return value method. So the correct child Component
@Subcomponent
public interface OtherFragmentComponent {
void inject(OtherFragment otherFragment);
@Subcomponent.Builder
interface Builder {
OtherFragmentComponent build(a); }}Copy the code
Next comes the parent Component, whose return value is, of course, our Builder.
@Component(modules = HstudyActivityModule.class)
public interface HstudyActivityComponent {
OtherFragmentComponent.Builder sonbuilder(a);
}
Copy the code
Can’t return child Component. If we return the child Component, we’ll tell you that the runtime error is as follows:
Components may not have factory methods for subcomponents that define a builder.
With @subComponent. Builder,Component has no factory mode method to create our child Component. Ok, that’s it, please forgive my Cet-4!!
The Activity, as before, initializes our parent Component and returns it via methods. Fragment uses the following dependencies
public class OtherFragment extends BaseFragment {
@Inject
Human human;
@Override
protected void processLogic(Bundle savedInstanceState) {
HstudyActivityComponent hstudyActivityComponent = ((HstudyActivity) getActivity()).getHstudyActivityComponent();
hstudyActivityComponent.
sonbuilder().build().inject(this); }}Copy the code
Ok, round and round, it’s working! See here to get a general idea of Dagger2.
Because it is used more in Java. The tentative decision is to divide Java into two parts. Otherwise the blog will be too long and nobody will read it.
This article github Demo address
A lot of work. Don’t get me wrong, not for money. Can you leave a footprint star