当前位置:网站首页>Analysis of dagger2 principle

Analysis of dagger2 principle

2022-07-05 03:40:00 Internet Panda

One 、 What is? Dagger2

Dagger2 yes Dagger Upgraded version , It's a dependency injection framework , Now by Google Take over maintenance . Okay , Here is a keyword dependency injection , So we have to know what dependency injection is first , In order to better understand Dagger2.

Dependency injection is a design pattern of object-oriented programming , The goal is to reduce program coupling , This coupling is caused by the dependencies between classes .

for instance : When we write object-oriented programs , Combinations are often used , That is, reference another class in one class , Thus, the method of the referenced class can be called to complete some functions , It looks like this .

public class ClassA {
    
    ...
    ClassB b;
    ...
    public ClassA() {
    
        b = new ClassB();
    }
    
    public void do() {
    
        ...
        b.doSomething();
        ...
    }
}

At this time, the problem of dependency arises ,ClassA Depend on ClassB, Must use ClassB Methods , To complete some functions . There seems to be no problem in this way , But we are ClassA Directly create ClassB Example , That's the problem , stay ClassA Directly create ClassB example , Against the principle of single responsibility ,ClassB Instance creation should not be ClassA To complete ; Secondly, the coupling degree increases , Expandability of , If we want to instantiate ClassB The parameter is passed in when the , Then we have to change ClassA Construction method of , Inconsistent with the open and close principle .

So we need an injection method , Inject dependencies into host classes ( Or called target class ) in , So as to solve the problems mentioned above . There are several ways of dependency injection :

  • Injection through interface
interface ClassBInterface {
    
    void setB(ClassB b);
}

public class ClassA implements ClassBInterface {
    
    ClassB classB;
    
    @override
    void setB(ClassB b) {
    
        classB = b;
    }
}
  • adopt set Methods to inject
public class ClassA {
    
    ClassB classB;
    
    public void setClassB(ClassB b) {
    
        classB = b;
    }
}
  • Injected by constructor
public class ClassA {
    
    ClassB classB;
    
    public void ClassA(ClassB b) {
    
        classB = b;
    }
  • adopt Java annotation
public class ClassA {
    
    // Injection will not be completed at this time , We also need the support of dependency injection framework , Such as RoboGuice,Dagger2
    @inject ClassB classB;
    
    ...
    public ClassA() {
    }

stay Dagger2 The last injection method is used in , By way of annotation , Inject dependencies into the host class .

Two 、 How to introduce Dagger2

To configure apt plug-in unit ( stay build.gradle(Project:xxx) Add the following code to )

dependencies {
    
    classpath 'com.android.tools.build:gradle:2.1.0'
    // add to apt plug-in unit 
    classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
   
}

Add dependency ( stay build.gradle(Module:app) Add the following code to )

apply plugin: 'com.android.application'

// Add the following code , application apt plug-in unit

apply plugin: 'com.neenbedankt.android-apt'
...
dependencies {
    
    ...
    compile 'com.google.dagger:dagger:2.4'
    apt 'com.google.dagger:dagger-compiler:2.4'
    //java annotation 
    compile 'org.glassfish:javax.annotation:10.0-b28'
    ...
}

3、 ... and 、 Use Dagger2

Here is a chestnut to illustrate , How to use Dagger2, It should be noted that , This chestnut is based on mvp Mode , So if you don't understand mvp Words , You can learn about mvp, Continue to read the following .

stay mvp in , The most common kind of dependency , Namely Activity hold presenter References to , And in Activity Instantiate this presenter, namely Activity rely on presenter,presenter We need to rely on View Interface , To update UI, It looks like this :

public class MainActivity extends AppCompatActivity implements MainContract.View {
    
    private MainPresenter mainPresenter;
    ...
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // Instantiation presenter  take view Pass to presenter
        mainPresenter = new MainPresenter(this);
        // call Presenter Method load data 
         mainPresenter.loadData();
         
         ...
    }

}

public class MainPresenter {
    
    //MainContract It's an interface ,View It's his internal interface , Here as View Interface can 
    private MainContract.View mView;
    
    MainPresenter(MainContract.View view) {
    
        mView = view;
    }
    
    public void loadData() {
    
        // call model Layer method , Load data 
        ...
        // When the callback method succeeds 
        mView.updateUI();
    }

such Activity And presenter Just coupled together , When it needs to change presenter The construction method of , You need to modify the code here . If you use dependency injection , That's true :

public class MainActivity extends AppCompatActivity implements MainContract.View {
    
    @Inject
    MainPresenter 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 Presenter Method load data 
         mainPresenter.loadData();
         
         ...
    }

}


public class MainPresenter {
    
    //MainContract It's an interface ,View It's his internal interface , Here as View Interface can 
    private MainContract.View mView;
    
    @Inject
    MainPresenter(MainContract.View view) {
    
        mView = view;
    }    
    public void loadData() {
    
        // call model Layer method , Load 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() {
    
        return mView;
    }
}

@Component(modules = MainModule.class)
public interface MainComponent {
    
    void inject(MainActivity activity);
}

forehead , It seems to be getting more complicated , Better not Dagger2 Well . But it's understandable to think about it carefully , Although the direct combination method is simple , But it has coupling , To solve this coupling , There may be more auxiliary classes , Let this direct dependency , Become indirect , To reduce the coupling . Like most design patterns , In order to achieve high cohesion and low coupling , There are often many interfaces and classes ,Daager2 So it is with , Although it seems complicated , But it's worth it in software engineering . below , Let's analyze what the above code means .

We see first MainActivity Code in , It was stated directly before MainPresenter, Now add a comment on the basis of the declaration @Inject, indicate MainPresenter It needs to be injected into MainActivity in , namely MainActivity Depend on MainPresenter, What we should pay attention to here is , Use @Inject when , Out-of-service private Modifiers modify the member properties of a class .

And then we were in MainPresenter The constructor of is also added @Inject annotation . such MainActivity Inside mainPresenter With his constructor . This connection can be understood in this way , When you see a class being @Inject When the tag , It will be in his construction method , If this construction method is also @Inject Marked words , This class will be initialized automatically , To complete dependency injection .

then , They will not establish contact with each other out of thin air , So we think of , A bridge is definitely needed , Connect them , That's what we're going to introduce Component.

Component It's an interface or an abstract class , use @Component annotations ( Let's ignore the bracketed modules), We define a inject() Method , Parameter is Mainactivity. then rebuild Next item , It will generate one with Dagger Prefixed Component class , Here is DaggerMainComponent, And then in MainActivity Complete the following code in .

DaggerMainComponent.builder()
                .mainModule(new MainModule(this))
                .build()
                .inject(this);

here Component will @Inject Annotated mainPresenter Associated with its constructor . here , See here , If you are a beginner , It must be very confusing , How on earth did they establish contact , Where is the instantiation process ? Don't worry. , The principle of this process will be explained later .

By this time we have finished presenter The injection process , But we found another MainModule class , What does this class do ?MainModlue Is an annotation class , use @Module annotations , Mainly used to provide dependencies . wait , Just passed @Inject You can complete the dependency , Why is it used here Module Class to provide dependencies ? It has been Module Class is mainly used to provide the dependencies of classes without constructors , These classes cannot be used @Inject mark , For example, third-party class libraries , System class , And the above example View Interface .

We are MainModule Class declares MainContract.View Member attribute , It is transmitted from the outside in the construction method view Assign a value to mView, And through a @Provides Marked with provide Opening method , Put this view return , This is based on provide The first method is to provide dependencies , We can create multiple methods to provide different dependencies . So how does this class work ? You can think of the above @Component Annotate the things in brackets . This is the next one

@Component(modules = MainModule.class)
public interface MainComponent {
    
    void inject(MainActivity activity);
}

therefore Module To make a difference , It still depends on Component class , One Component Class can contain multiple Module class , Used to provide dependency . Let's move on to the following code :

DaggerMainComponent.builder()
                .mainModule(new MainModule(this))
                .build()
                .inject(this);

Through here new MainModule(this) take view Pass on to MainModule in , then MainModule Inside provideMainView() Method returns this View, When de instantiating MainPresenter when , Found that the constructor has a parameter , It will be in Module Find the method to provide this dependency in , Will be View Pass in , That's it presenter in View The injection of .

Let's go through the above injection process again , First, clarify the following concepts :

  • @Inject A property or constructor with this annotation will participate in dependency injection ,Dagger2 Will instantiate the class with this annotation
  • @Module Classes with this annotation , Used to provide dependency , There are some definitions in it @Provides The purpose of the note is to provide Opening method , These are the dependencies provided ,Dagger2 You will find the dependencies needed to instantiate a class in this class .
  • @Component Used to put @Inject and @Module A bridge of connection , from @Module Get the dependency in and inject the dependency into @Inject

Then let's review the injection process above : First MainActivity Need to rely on MainPresenter, therefore , We use it inside @Inject Yes MainPresenter Annotate , Indicates that this is the class to be injected . then , We are right. MainPresenter The constructor of is also annotated @Inject, At this time, there is a parameter in the constructor MainContract.View, because MainPresenter Need to rely on MainContract.View, So we define a class , be called MainModule, Provide a method provideMainView, To provide this dependency , This MainView It's through MainModule Constructor injected into , Then we need to define Component Interface class , And will Module Include , namely

@Component(modules = MainModule.class)
public interface MainComponent {
    
    void inject(MainActivity activity);
}

At the same time, it declares a inject Method , Method parameters are ManActivity, Used to obtain MainActivity example , To initialize the MainPresenter

DaggerMainComponent.builder()
                .mainModule(new MainModule(this))
                .build()
                .inject(this);

here , The injection process is complete , Maybe by this time , There will still be some doubts , Because we can't see where the instantiation process is , Why write code like this , So below , Based on this example , analysis Dagger2 What has been done inside .

Four 、Dagger2 Principle of injection

Dagger2 Unlike other dependency injection frameworks , It's through apt The plug-in generates the corresponding injection code at the compilation stage , Let's take a specific look at Dagger2 What injection code is generated ?

We see first MainPresenter This class , In this class, we use @Inject mark , then Rebuild Project,Dagger2 Will be in /app/build/generated/apt/debug/ Generate a corresponding factory class under the directory MainPresenter_Factory, Let's look at the following specific code ( For ease of understanding , I put MainPresenter Also posted )

public class MainPresenter {
    
    MainContract.View mView;
    @Inject
    MainPresenter(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) {
    
    assert viewProvider != null;
    this.viewProvider = viewProvider;
  }

  @Override
  public MainPresenter get() {
    
    return new MainPresenter(viewProvider.get());
  }

  public static Factory<MainPresenter> create(Provider<MainContract.View> viewProvider) {
    
    return new MainPresenter_Factory(viewProvider);
  }
}

contrast MainPresenter, We found out MainPre_Factory The corresponding code is also generated in . First of all viewProvide, This is a Provider type , Generic parameters are our MainContract.View, Then through the construction method , Yes viewProvider instantiate . In fact, there is a doubt here , Why is the above member attribute not directly MainContract.View, It is Provider type ? notice provider We should think of this MainContract.View It's a dependency , And the provider we depend on is MainModule, So the viewProvider It must be from MainModul Provided . Let's continue to look at the following get() Method , See this method , I think we have a feeling of enlightenment , original MainPresenter The instantiation of is here , The parameters in the constructor are what we depend on MainContract.View, It is from viewProvider adopt get() Provide . Then there is a create() Method , And there is a parameter viewProvider, Used to create this MainPresenter_Factory class .

From above we can see that ,viewProvider By MainModule Provided , So let's take a look at MainModule The corresponding injection class .

@Module
public class MainModule {
    
    private final MainContract.View mView;

    public MainModule(MainContract.View view) {
    
        mView = view;
    }

    @Provides
    MainContract.View provideMainView() {
    
        return mView;
   }   
}


public final class MainModule_ProvideMainViewFactory implements Factory<MainContract.View> {
    
  private final MainModule module;

  public MainModule_ProvideMainViewFactory(MainModule module) {
    
    assert module != null;
    this.module = module;
  }

  @Override
  public MainContract.View get() {
    
    return Preconditions.checkNotNull(
        module.provideMainView(), "Cannot return null from a [email protected] @Provides method");
  }

  public static Factory<MainContract.View> create(MainModule module) {
    
    return new MainModule_ProvideMainViewFactory(module);
  }
}

See the class name above , We found a correspondence , stay MainModule As defined in @Provides The decorated method will correspondingly generate a factory class , Here is MainModule_ProvideMainViewFactory. We see one in this class get() Method , Which calls MainModule Inside provideMainView() Method to return the dependencies we need MainContract.View. Remember when MainPresenter_Factory Inside get() In the method , Instantiation MainPresenter Time parameters viewProvider.get() Do you ? By this point, we will understand , The original viewProvider That's generated MainModule_ProvideMainViewFactory, And then invoked it. get() Method , Will be what we need MainContract.View Injection into MainPresenter in .

Seeing this, we should understand MainPresenter The instantiation process .MainPresenter There will be a corresponding factory class , In this class get() Method MainPresenter establish , and MainPresenter The required View rely on , By MainModule As defined in provide The factory class corresponding to the method at the beginning provides .

Although we understand the creation process of instantiation , But at this time, I'm still a little confused ,MainPresenter_Factory Created by create() Accomplished , that crate Where was it called , The MainPresenter How does the instance relate to @Inject Annotated MainPresenter Related to , I think you already know the answer , That's right is Component. As I said before Component Is the connection @Module and @Inject The bridge , So the above doubts will be after compilation Component Find the answer in the corresponding class .

@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) {
    
    assert builder != 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;
    }
  }
}

From the code above, we can see the definition MainComponent Will generate a corresponding DaggerMainComponent, And implemented MainComponent The method in . We see it in the code again Provide Member properties of type , I said this before Provide Type is the dependency provided , We are looking at where they are instantiated . We see a 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);
  }

Seeing this, I guess I understand my doubts just now . First, I created MainModule_ProvideMainViewFactory example , To provide with MainContract.View rely on . There may be a little doubt here ,create() Method returns Factory type , and provideMainViewProvider It's a Provider type , In fact, you can see from the source code ,Factory Inherited from Provider.

public interface Factory<T> extends Provider<T> {
    
}

And then provideMainViewProvider Pass on to MainPresenter_Factory in , That is, as mentioned above viewProvider. And then put this mainPresenterProvider And then to MainActivity_MembersInjector To instantiate , We see that the front of this class is MainActivity start , So we can think of MainActivity The corresponding injection class , We will analyze this class later .

Then we are MainComponent The definition of Inject Method implementation , Here we call mainActivityMembersInjector.injectMembers(activity) Method , Take us MainActivity Inject into this class .

Then there is Builder Inner class , Used to create our module And its own instance . So in DaggerMainComponent Is mainly used to initialize dependencies , And really rely on Inject What is related is just MainActivity_MembersInjector class , Let's see what's done in this class .

public final class MainActivity_MembersInjector implements MembersInjector<MainActivity> {
    
  private final Provider<MainPresenter> mainPresenterProvider;

  public MainActivity_MembersInjector(Provider<MainPresenter> mainPresenterProvider) {
    
    assert mainPresenterProvider != 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();
  }
}

The key to this class is injectMembers() Method , Remember where this method is called ? I'm sure you remember , Just mentioned DaggerMainComponent Class inject() In the method , So here instance The example is by DaggerMainComponent Provided , Then we see the most critical code

instance.mainPresenter = mainPresenterProvider.get();
See this , I think everything should be clear , take mainPresenterProvider Created in MainPresenter Instance assigned to instance(MainActivity) Members of mainPresenter, So we use @Inject Dimensioned mainPresenter You get instantiation , Then it can be used in the code .

Come here , That's the end of the analysis Dagger2 The injection process , If you don't look at these generated classes , It's hard to understand how the whole process happened , As a result, I still don't know how to use this dependency injection framework . So it is very important to understand the internal implementation principle , At the beginning of learning, I was also confused , I can't figure out the relationship between them , I don't know how to write , After understanding the whole context , Find out how to use .

About Dagger2 I won't talk about other uses of , You can see other excellent blogs , I will attach a link later , Easy to learn .Dagger2 It is a dependency injection tool , As for how to use it, it's entirely personal , So don't worry about how to write correctly , Just understand the principle , Flexible use of , Be able to decouple as much as possible , The best way to write it is to suit your own business .

原网站

版权声明
本文为[Internet Panda]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/02/202202140727227365.html