Dagger2 Operating Principle Analysis and MVP case, dagger2mvp

Source: Internet
Author: User

Dagger2 Operating Principle Analysis and MVP case, dagger2mvp

Dagger2:

Purpose: Assume that Class A, Class B, and Class A contain an instance of Class B.

There are several ways to generate this relationship. Method 1:. setB (B B); Method 2: Input B, public A (B B) {} in the constructor of ){}. no matter what method is used, new B () is required (). if your business needs change on any day, you need to change all the new B () locations with strong coupling. After using dagger dependency injection, if you need to modify the B object later, there will be few changes, reducing coupling and writing new B () code.

Framework process:

It is effective during compilation and relevant code is automatically generated.

First, add the relevant jar package and apt plug-in dependencies.

Class B:

Public class QueryModel {

@ Inject // This indicates that QueryModel can be injected with dependencies. This constructor will be injected.

PublicQueryModel (){

}

Publicvoid queryNum (String number, IQueryListener listener ){

For (int I = 0; I <10000; I ++ ){

}

Listener. querySuccess ();

}

}

Class:

Public class QueryPresenter implementsIQueryListener {

PrivateIQueryView view;

@ Inject: This example can be injected with dependencies.

QueryModel model;

@ Inject

PublicQueryPresenter (IQueryView view ){

This. view = view;

DaggerQueryProcessorComponent. builder (). build (). inject (this); // start the injection.

}

Publicvoid startQuery (String number ){

Model. queryNum (number, this );

}

@ Override

Publicvoid querySuccess (){

View. showSuccessMsg ();

}

@ Override

Publicvoid queryFail (){

View. showErrorMsg ();

}

}

Class C:

@ Component // which object requires dependency injection requires corresponding Component components. It must be written as interface or abstractclass. the following uses the relationship between Class A and Class B as an example of Class A containing Class B. The example of Class B is obtained through dependency injection, so A needs this Component. the component automatically generated by the framework is the container instance: Dagger + component name, in which DaggerQueryProcessorComponent

@ Component

Public interface QueryProcessorComponent {

Voidinject (QueryPresenter presenter); // pay attention to the injection parameter. If the injection parameter declares any type, it must be injected to any type. A parent class cannot be declared here, and runs to the subclass to inject.

}

The most simple dagger framework is used as above. The red annotations have been briefly described. When Class B needs to be injected, add a @ Inject annotation to the constructor of class B. Add a @ Inject parameter to the declared variable where it is used. In this case, the problem is that only the classes we write can be injected. If it is an android. jar class or a third-party jar class, we cannot inject it. How can this problem be solved? @ Module and @ Provides annotations are required.

@ Module module is used to provide the instance objects required for dagger annotation (the instance cannot be obtained by adding @ inject to the constructor). @ module is used to provide the annotation of the Instance class, @ provides is used to annotate the methods used to provide class instances. It seems that the injection framework first looks for the module to obtain the example to be injected. If it is not found in the module, it then looks for the constructor.

Another point is that in the simplest dagger framework example, the constructor QueryModel () does not contain parameters. What if a parameter is included? The @ module and @ provides are used to provide parameters. (when the dagger framework injects an object, if the constructors of classes annotated with @ module require parameters, they must pass. appModule (newModule (parameter )).

@ Module

Public class ActivityModule {

PrivateIQueryView view;

PrivateContext context;

PublicActivityModule (IQueryView, Context context ){

This. view = view;

This. context = context;

}

@ Provides

IQueryViewprovideIQueryView (){

Returnview;

}

@ Provides

SharedPreferencesproviderGlobalSharedPreference (){

ReturnPreferenceManager. getdefasharsharedpreferences (context );

}

}

@ Component (modules = ActivityModule. class)

Public interface ActivityComponent {

Voidinject (DaggerActivity activity );

}

// There can also be multiple modules here. In short, the parameters obtained through module for the activity dependent injection objects in voidinject (DaggerActivity) must be listed in the modudles attribute.

Activity with dependency injection:

Public class DaggerActivity extendsAppCompatActivity implements IQueryView {

@ Inject

QueryPresentermPresenter; // because the presenter constructor requires parameters, you need to provide a method for the daggerActivity injection container to provide the parameters required to generate the presenter.

@ Override

Protectedvoid onCreate (Bundle savedInstanceState ){

Super. onCreate (savedInstanceState );

SetContentView (R. layout. activity_dagger );

This class is automatically generated.

DaggerNewActivityComponent. builder ().ActivityModule (newActivityModule (this, this )). Build (). inject (this );//

// MPresenter = new QueryPresenter (this );

// DaggerActivityComponent. builder (). build (). inject (this); first

// Compare the two injection methods and find that if a parameter is required, you need to insert the corresponding module (that is, the module that provides the parameter) between builder () and build ), note: The activityModule () method is automatically generated by the framework.

}

@ Override

Publicvoid showSuccessMsg (){

Toast. makeText (DaggerActivity. this, "success", Toast. LENGTH_SHORT). show ();

}

@ Override

Publicvoid showErrorMsg (){

Toast. makeText (DaggerActivity. this, "failed", Toast. LENGTH_SHORT). show ();

}

}

**************************************** ***********

Dagger2 code injection process analysis after compilation: This is not the principle. This is based on the Code automatically generated by the dagger framework for Process Analysis.

The starting point of the analysis is: Write an instance B of Class B in Class A to analyze how this B is generated. According to the injection code

DaggerAppComponent. builder ().

AppModule (newAppModule (getApplicationContext ())).

HttpModule (newHttpModule (getApplicationContext (). build (). inject (this); you can execute this code.

First, let's look at its internal class Builder:

Public static final class Builder {

Private HttpModule httpModule; // each module you add corresponds

Private AppModule appModule;

Private Builder (){}

PublicAppComponent build () {// when the build () method is called, The system checks whether each set module exists. If not, an exception is thrown.

If (httpModule = null ){

Throw new IllegalStateException (HttpModule. class. getCanonicalName () + "must be set ");

}

If (appModule = null ){

Throw new IllegalStateException (AppModule. class. getCanonicalName () + "must be set ");

}

Return new DaggerAppComponent (this); // you need to analyze it here.

}

PublicBuilder appModule (AppModule appModule ){

This. appModule = Preconditions. checkNotNull (appModule );

Return this;

}

PublicBuilder httpModule (HttpModule httpModule ){

This. httpModule = Preconditions. checkNotNull (httpModule );

Return this;

}

}

DaggerAppComponent's own constructor:

Private DaggerAppComponent (Builder builder ){

Assertbuilder! = Null;

Initialize (builder );

}

Initialize (builder );

@ SuppressWarnings ("unchecked ")

Privatevoid initialize (final Builder builder ){

* All objects include the object instance provider.

**************************************** **********

This. provideTestInstanceProvider =

HttpModule_ProvideTestInstanceFactory.create (builder. httpModule); this is also the key method. Corresponding to our added @ provides Annotation

This. provideSharedPrefProvider = AppModule_ProvideSharedPrefFactory.create (builder. appModule );

**************************************** ***

The injector is the real executor of the injection. In create (), all the objects that need to be added to the container are passed in through parameters.

This. baseMvpActivityMembersInjector =

BaseMvpActivity_MembersInjector.create (

User_Factory.create (), provideTestInstanceProvider, provideSharedPrefProvider );

}

After analysis, we can find that a factory method is generated for each provider method in each module. Take one of them as an example:

Public final classAppModule_ProvideSharedPrefFactory implements Factory {

Privatefinal AppModule;

Publicappmodule_providesharedpreffacloud (AppModule module ){

Assertmodule! = Null;

This. module = module;

}

@ Override

PublicSharedPreferences get () {// here get () calls the passed module. We annotated the @ provide method, so that we can get () through the factory object () obtain the defined annotation object.

ReturnPreconditions. checkNotNull (

Module. provideSharedPref (), "Cannot return null from anon-@ Nullable @ Provides method ");

}

Publicstatic Factory Create (AppModule module ){

Returnnew appmodule_providesharedpreffacloud (module );

}

}

Next, the key is the inject (this) at the end of the call chain ):

@ Override

Public void inject (BaseMvpActivity mvpActivity ){

BaseMvpActivityMembersInjector. injectMembers (mvpActivity );

}

Public BaseMvpActivity_MembersInjector (

Provider UserProvider,

Provider TestInstanceProvider,

Provider SharedPreferencesProvider ){

AssertuserProvider! = Null;

This. userProvider = userProvider;

AsserttestInstanceProvider! = Null;

This. testInstanceProvider = testInstanceProvider;

AssertsharedPreferencesProvider! = Null;

This. sharedPreferencesProvider = sharedPreferencesProvider;

}

Publicstatic MembersInjector Create (

Provider UserProvider,

Provider TestInstanceProvider,

Provider SharedPreferencesProvider ){

Returnnew BaseMvpActivity_MembersInjector (

UserProvider, testInstanceProvider, sharedPreferencesProvider );

}

@ Override

Public void injectMembers (BaseMvpActivityinstance ){

If (instance = null ){

Throw newNullPointerException ("Cannot inject members into a null reference ");

}

Here, we will assign a value to the dependent injection objects marked by @ inject in BaseMvpActivity. This completes the entire dependency injection process.

Instance. user = userProvider. get ();

Instance. testInstance = testInstanceProvider. get ();

Instance. sharedPreferences = sharedPreferencesProvider. get ();

}

Publicstatic void injectUser (BaseMvpActivity instance, Provider UserProvider ){

Instance. user = userProvider. get ();

}

Publicstatic void injectTestInstance (

BaseMvpActivity instance, Provider TestInstanceProvider ){

Instance. testInstance = testInstanceProvider. get ();

}

Publicstatic void injectSharedPreferences (

BaseMvpActivity instance, Provider SharedPreferencesProvider ){

Instance. sharedPreferences = sharedPreferencesProvider. get ();

}

}

Use uml to indicate the following:

Let's talk about the use of @ Scope annotation:

Scope literally refers to Scope, which can be understood as Scope in java.

In android, some objects are global applications, and some objects may share multiple activities. In this case, the scope annotation is required. Note: If you use a singleton for self-written objects, you need to provide the provide method in the module, which cannot be provided through the inject constructor.

The procedure is as follows:

1. Create a scope with random names and fixed forms.


@ Scope

@ Retention (RetentionPolicy. RUNTIME)

Public @ interface ActivityScope {

}

2. Add the scope annotation to the provide method in the module where the scope needs to be used.

@ Module

Public class AppModule {

Private Context context;

PublicAppModule (Context context ){

This. context = context;

}

@ Provides

@ ActivityScope // Add the scope Annotation

User providersssssssUser (){

Return new User ();

}

}
3. Add scope to the declared annotation container.

@ ActivityScope

@ Component (modules = {AppModule. class, HttpModule. class}) // If the module uses the scope, the scope must be written in the container; otherwise, it cannot be compiled.

Public interface AppComponent {

//

Voidinject (BaseMvpActivity mvpActivity );

Voidinject (BaseMvp2Activity second_activity );

}

4. Generate a container in the application (ensure that the container can be instantiated only once, otherwise it cannot be a singleton)

Public class DaggerApplication extendsApplication {

AppComponent appComponent;

@ Override

Publicvoid onCreate (){

Super. onCreate ();

GetAppComponent ();

}

PublicAppComponent getAppComponent (){

If (appComponent = null ){

AppComponent = DaggerAppComponent. builder ().

AppModule (newAppModule (getApplicationContext ())).

HttpModule (newHttpModule (getApplicationContext (). build ();

}

Return appComponent;

}

}

5. Inject in the activity to be injected.

(DaggerApplication) getApplication (). getAppComponent (). inject (this );

In this way, we can ensure that the two activities are the same user object.

The code generated by the dagger framework can be used to analyze the singleton by adding scope. The analysis is based on the user object.

In the previous code, the automatically generated annotation container object DaggerAppComponent generates a Provider for every provide method in the module. And then get the corresponding instance through the provider. get () method.

When we do not add the scope annotation, The usrprovider generated code is as follows:

UserProvider = appmodule_providersssssssssuserfactory.create (builder. appModule );

When a user instance is required, appmodule is called. providerUser () actually generates a user instance. If you call inject () multiple times, multiple user objects will be generated, which is not a singleton.

After the scope operation is added, the code generated by the Framework is as follows.

Userprovider = DoubleCheck. provider (AppModule_ProvidersssssssUserFactory.create (builder. appModule ));

Usrprovider is encapsulated with a doublecheck. When userprovider. get () is called, doubleCheck. get () is actually called. Next, let's take a look at the specific implementation of doubleCheck. get.

Private static final Object UNINITIALIZED = newObject ();

Privatevolatile Object instance = UNINITIALIZED;

@ SuppressWarnings ("unchecked") // castonly happens when result comes from the provider

@ Override

Public Tget (){

Object result = instance;

If (result = UNINITIALIZED) {check whether the result has been initialized.

Synchronized (this) {// Code Synchronization, which also ensures thread security.

Result = instance;

If (result = UNINITIALIZED ){

Instance = result = provider. get ();

/* Null out the reference to the provider. We are never going to need itagain, so we

* Can make it eligible for GC .*/

Provider = null;

}

}

}

Return (T) result;

}

Therefore, when we inject a user object in different places (that is, when the user object is obtained), if the object has been generated, it will be directly returned, ensuring the Singleton.

There are also some uncommon Annotations:

@ Dependencies @ Named

@ Dependencies: used for dependencies between components.

Purpose: assume that I have an application-level component (the injected objects must have sharedPreference \ context \ login users of a single instance, and so on ), the component at the activity level will certainly use these instance objects. At this time, the activity component needs to depend on the application component.

Statement:

@ Component (dependencies = {Appcomponent. class}, modules = {})

Public interfaceActivityComponent {

}

Rule: 1. If activitycomponent wants to use sharedPreference in application component, a sharedpreference method must be provided in application component.

@ ActivityScope

@ Component (dependencies ={}, modules = {AppModule. class, HttpModule. class })

Public interfaceAppComponent {

// Void inject (BaseMvpActivity mvpActivity );

Void inject (BaseMvp2Activitysecond_activity );

SharedPreferences ();

}

2. dependencies must be different from the scope of the dependent component.

@ Named: used to differentiate multiple instances of the same type.

Purpose: Suppose I want to provide two sharedpreference types, one is the default one and the other is the cache. We can write two methods in the AppModule to provide two different sharedpreference methods.

AppModule:

@ Named ("default ")

@ Provides

SharedPreferences provideSharedPref (){

Return PreferenceManager. getdefasharsharedpreferences (context );

}

@ Named ("cached ")

@ Provides

SharedPreferences provideCachedSharedPref (){

Return context. getSharedPreferences ("cached", Context. MODE_PRIVATE );

}

Add the @ named annotation to the Instance name of the injected place to obtain the specified sharedpreference.

MainActivity:

@ Named ("cached ")

@ Inject

Protected SharedPreferences sharedPreferences;

@ Named ("default ")

@ Inject

Protected SharedPreferences sharedPreferencesDefault;

Note that if there is a dependency, activitycomponent uses the default and cached sharedpreference, because the application component needs to expose the object method, now we need to provide two methods for @ Named.

ApplicationComponent:

@ Named ("cached ")

SharedPreferences ();

@ Named ("default ")

SharedPreferences SharedPreferences2 ();

The usage of basic dagger2 is analyzed.


Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.