Reprinted from: blog.csdn.net/hsk256/arti…
Dagger2 is now being used more and more in projects. Recently, I spent some time learning about Dagger2. This article mainly helps to understand the implementation process of Dagger2 injection.
What is a Dagger2
Dagger2, an updated version of Dagger, is a dependency injection framework now maintained by Google. Well, there is a keyword dependency injection, so we need to know what dependency injection is to understand Dagger2 better.
Dependency injection (DI) is a design pattern in object-oriented programming that aims to reduce program coupling, which is caused by dependencies between classes.
For example, when we write object-oriented programs, we often use composition, that is, referring to another class in one class so that we can call the methods of the referenced class to do something like this.
public class ClassA {. ClassB b; .public ClassA() {
b = new ClassB();
}
public void do() {... b.doSomething(); . }}
Copy the code
This is where the dependency problem arises. ClassA depends on ClassB, and you have to use ClassB’s methods to do something. This doesn’t seem to be a problem, but we created an instance of ClassB directly in the ClassA constructor. This is where the problem arises. Creating an instance of ClassB directly in ClassA violates the single responsibility principle. Secondly, the coupling degree increases and the scalability is poor. If we want to pass in parameters when instantiating ClassB, we have to change the constructor of ClassA, which does not match the open and close principle.
So we need a way to inject dependencies into the host class (or target class) to solve the problem described above. Dependency injection can be done in several ways:
-
Injection through the interface
interface ClassBInterface { void setB(ClassB b); } public class ClassA implements ClassBInterface { ClassB classB; @override voidsetB(ClassB b) { classB = b; }} Copy the code
-
Injection via the set method
public class ClassA { ClassB classB; public void setClassB(ClassB b) { classB = b; }} Copy the code
-
Injection via constructor
public class ClassA { ClassB classB; public void ClassA(ClassB b) { classB = b; } Copy the code
-
Through Java annotations
public class ClassA { // Injection will not be completed at this point. Dependency injection frameworks such as RoboGuice,Dagger2 are required @injectClassB classB; .public ClassA() {} Copy the code
The last type of injection is used in Dagger2, where dependencies are injected into the host class through annotations.
How to introduce Dagger2
-
Build. Gradle (Project: XXX)
dependencies { classpath 'com. Android. Tools. Build: gradle: 2.1.0' // Add apt plugin classpath 'com. Neenbedankt. Gradle. Plugins: android - apt: 1.8' } Copy the code
-
Add dependencies (build. Gradle (Module:app)
apply plugin: 'com.android.application' // Add the following code to apply apt plugin apply plugin: 'com.neenbedankt.android-apt'. dependencies { ... compile'com. Google. Dagger: a dagger: 2.4' apt 'com. Google. Dagger: a dagger - compiler: 2.4' / / Java annotations compile 'org. Anyone: javax.mail. Annotation: 10.0 - b28'. } Copy the code
Using Dagger2
Here is an example of how to use Dagger2. It should be noted that this chestnut is based on the MVP mode, so if you don’t know the MVP, you can learn about the MVP before moving on to the following content.
One of the most common dependencies in MVP is for an Activity to hold a reference to a Presenter and instantiate that presenter in an Activity. That is, an Activity relies on a Presenter, which in turn relies on the View interface to update its UI. Like this:
public class MainActivity extends AppCompatActivity implements MainContract.View {
privateMainPresenter mainPresenter; .@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Instantiate Presenter to pass view to presenter
mainPresenter = new MainPresenter(this);
Call the Presenter method to load datamainPresenter.loadData(); . }}public class MainPresenter {
//MainContract is an interface, and View is its internal interface
private MainContract.View mView;
MainPresenter(MainContract.View view) {
mView = view;
}
public void loadData() {
// Call the Model layer method to load the data.// When the callback method succeeds
mView.updateUI();
}
Copy the code
This way the Activity is only coupled to the Presenter, and when you need to change the presenter construction, you need to change the code here. With dependency injection, it looks like this:
public class MainActivity extends AppCompatActivity implements MainContract.View {
@InjectMainPresenter mainPresenter; .@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerMainComponent.builder()
.mainModule(new MainModule(this))
.build()
.inject(this);
Call the Presenter method to load datamainPresenter.loadData(); . }}public class MainPresenter {
//MainContract is an interface, and View is its internal interface
private MainContract.View mView;
@Inject
MainPresenter(MainContract.View view) {
mView = view;
}
public void loadData() {
// Call the Model layer method to load the data.// When the callback method succeeds
mView.updateUI();
}
@Module
public class MainModule {
private final MainContract.View mView;
public MainModule(MainContract.View view) {
mView = view;
}
@Provides
MainView provideMainView() {
returnmView; }}@Component(modules = MainModule.class)
public interface MainComponent {
void inject(MainActivity activity);
}
Copy the code
Well, it seems to be getting more complicated. Might as well not have used Dagger2. However, it is understandable to think about it carefully. Although the direct combination method is simple, it is coupled. In order to solve this coupling, more auxiliary classes may be generated, so that the direct dependence becomes indirect and the coupling is reduced. As with most design patterns, there are often many interfaces and classes to achieve high cohesion and low coupling, as with Daager2, which may seem a bit more complex, but is worth it in software engineering. Now, let’s analyze what the above code means.
Add Inject annotation @Inject into MainActivity.inject Inject into MainActivity.inject Inject into mainActivity.inject Inject into mainActivity.inject MainActivity depends on MainPresenter. Note that the @Inject modifier cannot be used to decorate the member properties of the class.
Then we add @Inject annotation to MainPresenter’s constructor as well. Thus the mainPresenter in the MainActivity establishes a connection with its constructor. When we see a class marked by @Inject, it goes to its constructor. If the constructor is also marked by @Inject, it automatically initializes the class to complete dependency injection.
However, they don’t connect in a vacuum, so we figured we’d need a bridge to connect them, and that’s the Component we’ll introduce next.
Component is an interface or abstract class annotated with the @Component annotation (regardless of modules in parentheses). Inside this interface we define a Inject () method with the parameter Mainactivity. Rebuild the project, which generates a Component class prefixed with Dagger, in this case DaggerMainComponent, and then complete the following code in the MainActivity.
DaggerMainComponent.builder()
.mainModule(new MainModule(this))
.build()
.inject(this);
Copy the code
Component now associates the @Inject annotated mainPresenter with its constructor. Now, if you look at this, if you’re a beginner, you’re going to be very confused, how do you make this connection, where is the instantiation? Don’t worry, we’ll explain how this works later.
At this point we have completed the presenter injection, but we find that there is also a MainModule class. What does this class do? MainModlue is an annotation class, annotated with the @Module annotation, mainly used to provide dependencies. Wait, why use the Module class to provide the dependency when we just provide the dependency via @Inject? The Main reason for the Module class is to provide dependencies on classes that have no constructors and cannot be annotated with @Inject, such as third-party class libraries, system classes, and the View interface in the example above.
We declare the MainModule mainContract. View member property, assign the View to the mView in the constructor, and return the View with a method starting with provide (@provides). The method that starts with provide provides dependencies, and we can create multiple methods to provide different dependencies. So how does this class work? You can think of the @Component annotation in parentheses mentioned above. That’s the one down here
@Component(modules = MainModule.class)
public interface MainComponent {
void inject(MainActivity activity);
}
Copy the code
So modules are still dependent on the Component class. A Component class can contain multiple Module classes to provide dependencies. Let’s look at the following code:
DaggerMainComponent.builder()
.mainModule(new MainModule(this))
.build()
.inject(this);
Copy the code
We pass the view to the MainModule via new MainModule(this), and the provideMainView() method in the MainModule returns the view. When we instantiate MainPresenter, we find that the constructor takes an argument, The View is injected into the Presenter View by looking for a method in the Module that provides the dependency and passing it in.
Let’s go through the injection process again, starting with the following concepts:
- Inject property or constructor with this annotation will participate in dependency injection, and Dagger2 instantiates the class with this annotation
- @Module is a class with this annotation that Provides dependencies. It defines methods that start with the provide annotation with the @provides annotation. These methods are the provided dependencies.
- @Component is the bridge between @Inject and @Module, taking dependencies from @Module and injecting them into @Inject
Let’s review the injection process above: First, MainActivity relies on MainPresenter, so we annotate MainPresenter with @Inject to indicate that it is the class to Inject. Add annotation @inject to the constructor of MainPresenter. The constructor has an argument mainContract. View. Since MainPresenter relies on MainContract.View, we define a class. Called MainModule, it provides a method, provideMainView, that provides the dependency. The MainView is injected through the constructor of the MainModule. Then we need to define the Component interface class and include the Module, i.e
@Component(modules = MainModule.class)
public interface MainComponent {
void inject(MainActivity activity);
}
Copy the code
It also declares a inject method with the parameter ManActivity, which gets the MainActivity instance and initializes the MainPresenter declared in it
DaggerMainComponent.builder()
.mainModule(new MainModule(this))
.build()
.inject(this);
Copy the code
At this point, the injection process is complete. At this point, it may still be a little confusing because we can’t see where the instantiation process is and why we write the code this way, so let’s take a look at what’s going on inside Dagger2 based on this example.
Dagger2 Injection principle
Dagger2 is different from other dependency injection frameworks. It uses apt plug-in to generate corresponding injection code at compile stage.
Let’s start with the MainPresenter class, where we annotate the constructor with @Inject, Rebuild Project, Dagger2 in/app/build/generated/apt/debug/directory generates a corresponding factory class MainPresenter_Factory, we look at specific code below (in order to facilitate understanding, I have also posted MainPresenter)
public class MainPresenter {
MainContract.View mView;
@InjectMainPresenter(MainContract.View view) { mView = view; }}public final class MainPresenter_Factory implements Factory<MainPresenter> {
private final Provider<MainContract.View> viewProvider;
public MainPresenter_Factory(Provider<MainContract.View> viewProvider) {
assertviewProvider ! =null;
this.viewProvider = viewProvider;
}
@Override
public MainPresenter get() {
return new MainPresenter(viewProvider.get());
}
public static Factory<MainPresenter> create(Provider<MainContract.View> viewProvider) {
return newMainPresenter_Factory(viewProvider); }}
Copy the code
In contrast to MainPresenter, we see that the corresponding code is also generated in MainPre_Factory. The first is viewProvide, which is a Provider type, and the generic argument is our MainContract.View, and then instantiate the viewProvider through the constructor. Provider: MainContract.View mainContract. View Provider: MainContract.View Provider: MainContract.View Provider: MainContract.View Provider When we look at providers we should remember that the MainContract.View is a dependency and the provider of the dependency is MainModule, so the viewProvider must be provided by MainModul. The constructor argument is the mainContract.view we rely on, which is provided by the viewProvider via get(). This is followed by a create() method with a viewProvider argument that creates the MainPresenter_Factory class.
The viewProvider is provided by MainModule, so let’s move on to the injected class for MainModule.
@Module
public class MainModule {
private final MainContract.View mView;
public MainModule(MainContract.View view) {
mView = view;
}
@Provides
MainContract.View provideMainView() {
returnmView; }}public final class MainModule_ProvideMainViewFactory implements Factory<MainContract.View> {
private final MainModule module;
public MainModule_ProvideMainViewFactory(MainModule module) {
assertmodule ! =null;
this.module = module;
}
@Override
public MainContract.View get() {
return Preconditions.checkNotNull(
module.provideMainView(), "Cannot return null from a non-@Nullable @Provides method");
}
public static Factory<MainContract.View> create(MainModule module) {
return newMainModule_ProvideMainViewFactory(module); }}
Copy the code
Looking at the class name above, we see a correspondence between the @provides method defined in MainModule and the corresponding factory class, in this case MainModule_ProvideMainViewFactory. We see that this class has a get() method that calls the provideMainView() method in the MainModule to return the dependency mainContract.view that we need. Remember the viewprovider.get () argument instantiated by MainPresenter_Factory in the get() method? The viewProvider is the generated MainModule_ProvideMainViewFactory, and then calls its get() method to inject the required MainContract.View into the MainPresenter.
This should clarify the instantiation process for MainPresenter. MainPresenter has a factory class that is created in the get() method of the class. The View dependencies that MainPresenter needs are provided by the factory class for the methods defined in the MainModule that start with provide.
The MainPresenter_Factory is created by Create (), so where does crate call it? The MainPresenter instance is associated with the @Inject annotated MainPresenter (Component). Component is the bridge between @Module and @Inject, so look in the compiled Component class to find the answer.
@Component(modules = MainModule.class)
public interface MainComponent {
void inject(MainActivity activity);
}
public final class DaggerMainComponent implements MainComponent {
private Provider<MainContract.View> provideMainViewProvider;
private Provider<MainPresenter> mainPresenterProvider;
private MembersInjector<MainActivity> mainActivityMembersInjector;
private DaggerMainComponent(Builder builder) {
assertbuilder ! =null;
initialize(builder);
}
public static Builder builder() {
return new Builder();
}
@SuppressWarnings("unchecked")
private void initialize(final Builder builder) {
this.provideMainViewProvider = MainModule_ProvideMainViewFactory.create(builder.mainModule);
this.mainPresenterProvider = MainPresenter_Factory.create(provideMainViewProvider);
this.mainActivityMembersInjector = MainActivity_MembersInjector.create(mainPresenterProvider);
}
@Override
public void inject(MainActivity activity) {
mainActivityMembersInjector.injectMembers(activity);
}
public static final class Builder {
private MainModule mainModule;
private Builder() {}
public MainComponent build() {
if (mainModule == null) {
throw new IllegalStateException(MainModule.class.getCanonicalName() + " must be set");
}
return new DaggerMainComponent(this);
}
public Builder mainModule(MainModule mainModule) {
this.mainModule = Preconditions.checkNotNull(mainModule);
return this; }}}
Copy the code
The above code shows that the defined MainComponent generates a corresponding DaggerMainComponent and implements the methods in MainComponent. We see the member properties of the Provide type again in the code. As described earlier, the Provide type is the provided dependency, and we are looking at where they are instantiated. We see that there is an initialize() method
@SuppressWarnings("unchecked")
private void initialize(final Builder builder) {
this.provideMainViewProvider = MainModule_ProvideMainViewFactory.create(builder.mainModule);
this.mainPresenterProvider = MainPresenter_Factory.create(provideMainViewProvider);
this.mainActivityMembersInjector = MainActivity_MembersInjector.create(mainPresenterProvider);
}
Copy the code
I guess that makes sense. An instance of MainModule_ProvideMainViewFactory was created to provide the MainContract.view dependency. The create() method returns a Factory. ProvideMainViewProvider is a Provider.
public interface Factory<T> extends Provider<T> {
}
Copy the code
We then pass the provideMainViewProvider to the MainPresenter_Factory, which is the viewProvider we talked about earlier. Then we pass the mainPresenterProvider to the MainActivity_MembersInjector to instantiate it. We see that this class starts with MainActivity, so we can think of MainActivity injecting the due class, We’ll examine this class later.
Then we defined in the MainComponent Inject method implementation, here called the mainActivityMembersInjector. InjectMembers (activity) method, will our MainActivity into the class.
Next comes the Builder inner class, which creates our Module and instances of itself. So the DaggerMainComponent is used to initialize dependencies, and the real thing that injects those dependencies is the MainActivity_MembersInjector class. Let’s see what this class does.
public final class MainActivity_MembersInjector implements MembersInjector<MainActivity> {
private final Provider<MainPresenter> mainPresenterProvider;
public MainActivity_MembersInjector(Provider<MainPresenter> mainPresenterProvider) {
assertmainPresenterProvider ! =null;
this.mainPresenterProvider = mainPresenterProvider;
}
public static MembersInjector<MainActivity> create(
Provider<MainPresenter> mainPresenterProvider) {
return new MainActivity_MembersInjector(mainPresenterProvider);
}
@Override
public void injectMembers(MainActivity instance) {
if (instance == null) {
throw new NullPointerException("Cannot inject members into a null reference");
}
instance.mainPresenter = mainPresenterProvider.get();
}
public static void injectMainPresenter( MainActivity instance, Provider<MainPresenter> mainPresenterProvider) { instance.mainPresenter = mainPresenterProvider.get(); }}
Copy the code
The key to this class is the injectMembers() method. Remember where it was called? I’m sure you remember it in the Inject () method of the DaggerMainComponent class, so here the instance is provided by the DaggerMainComponent, and then we see the key line
instance.mainPresenter = mainPresenterProvider.get();
Copy the code
Assign an instance of MainPresenter created in the mainPresenterProvider to the member of instance(MainActivity) MainPresenter. The mainPresenter we annotated with @Inject is instantiated and can then be used in our code.
At this point, we have analyzed the Dagger2 injection process, and without looking at the generated classes, it is hard to understand how the whole process happens and how to use the dependency injection framework. Therefore, it is very important to focus on understanding the internal implementation principle. At the beginning of learning, I was confused about the relationship between them and did not know how to write. After understanding the whole context, I found that I knew how to use it.
I won’t go into other uses of Dagger2, but check out other great blogs and I’ll link to them to make it easier to learn. Dagger2 is a dependency injection tool, and how to use it is entirely up to you, so you don’t have to worry about how to write it correctly. As long as you understand the principle, use it flexibly, and decouple it as much as possible, it is the best way to write it for your business.
Thanks to:
www.jianshu.com/p/cd2c1c9f6…
Alighters.com/blog/2016/0…
Chriszou.com/2016/05/10/…
Blog.nimbledroid.com/2016/03/07/…
google.github.io/dagger/
Disclaimer In order to avoid being accused of plagiarism, I attach my Jane book home page address and personal blog Jane book address personal blog address