Dagger2 Foundation and Advancement

Posted by expert_21 on Thu, 04 Jul 2019 20:45:33 +0200

Preface

Dagger2 relies on an injection framework to reduce program-to-program coupling and prevent potential systemic problems.
In addition, it has many advantages:
Increase development efficiency and eliminate repetitive simple manual labor

The process of starting a new instance is a repetitive and simple manual effort, and dagger2 can do exactly what a new instance does, so we focus on key businesses and also on increasing development efficiency.Leave out the method of writing a singleton, and don't worry about thread safety, lazy or hungry singletons.Because dagger2 can do all this work.

Better managed class instances

ApplicationComponent in each app manages the global class instances for the entire app, and all global class instances are unified for the ApplicationComponent management, and their lifecycle is the same as that of the app.Each page corresponds to its own Component, which manages all the class instances on which its own page depends.Because of Component, Module, the class instance structure of the entire app becomes clear.

decoupling

If dagger2 is not used, it is very likely that the new code for a class will be flooded with multiple classes of app. If the constructor for that class changes, all the classes involved will have to be modified.Design patterns promote encapsulation of easily changing parts.After we used dagger2.If class instances were created by using Inject annotated constructors, we would hardly need to modify any code, even if the constructor became overwhelming.If you create a class instance through a factory-mode Module, the Module actually encapsulates the code of the new class instance so that even if the class's constructor changes, you only need to modify the Module.
When a netizen asks a question like this, the constructor of the Module changes, and after the change, the class of the corresponding new Module changes, which does not achieve the effect of decoupling.First, decoupling does not mean that there really is no relationship between classes or modules at all. The purpose of decoupling is to minimize the impact of a class or module on the classes or modules that are associated with itself, or to say that there is no such impact at all. This is not possible.
The other benefit of decoupling is that it is easy to test, and if you need to replace it with a network test class, you only need to modify the corresponding Module.

1. Add dependencies first:

Project gradle:

 classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'

Module gradle:

    apply plugin: 'com.neenbedankt.android-apt'
    ....
    provided 'org.glassfish:javax.annotation:10.0-b28'
    compile 'com.google.dagger:dagger:2.5'
    compile 'com.google.dagger:dagger-compiler:2.5'

2. Simple use:

public class UserStore {
    public void register() {
    }
}

public class ApiService {
    public void register() {
    }
}

//Is the bridge between Inject and Provides
//This sentence relates to modules
@Component(modules = {UserModule.class})
public interface UserComponet {
    //This sentence relates to MainActivity
    void inject(MainActivity activity);
}

@Module
public class UserModule {
    //Provide Dependency
    @Provides
    public ApiService provideApiService() {
        return new ApiService();
    }
}


public class MainActivity extends AppCompatActivity {
    //Tell Dagger that this class needs to inject objects
    @Inject
    ApiService mApiService;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //Rebuild Project is required first
        DaggerUserComponet.create().inject(this);
        mApiService.register();
    }
}

UserStore is used to save user data locally, ApiService gets network data, UserManager manages these two objects, @Inject annotation is used to tell Dagger2 that this class requires dependent injection, @Component(modules = {UserModule.class}) is associated to UserModule, provideApiService() method in UserModule is mentionedFor instance objects, void inject(MainActivity activity) is used to associate activities so that @Inject is associated with @Module via @Component.
Common Notes:

3. Further use:

@Module
public class UserModule {


    private Context mContext;

    public UserModule(Context context) {
        this.mContext = context;
    }

    @Provides
    public UserStore providesUserStore() {
        return new UserStore(this.mContext);
    }

    @Provides
    public UserManager providesUserManager(ApiService apiService, UserStore userStore) {
        return new UserManager(apiService, userStore);
    }
}


public class UserStore {
    public UserStore(Context context) {
        Log.e("userstore","constructorUsertore");
    }
    public void register() {
    }
}


//Is the bridge between Inject and Provides
//This sentence relates to modules
@Component(modules = {UserModule.class})
public interface UserComponet {
    //This sentence relates to MainActivity
    void inject(MainActivity activity);
}



public class ApiService {
    //When a method to return an ApiService instance is not provided in the UserModule, the construction method for the @Inject label in this class is sought
    @Inject
    public ApiService(){
        Log.e("ApiService","constructorApiService");
    }

    public void register() {
    }
}


public class UserManager {
    private ApiService mApiService;
    private UserStore mUserStore;

    public UserManager(ApiService mApiService, UserStore mUserStore) {
        this.mApiService = mApiService;
        this.mUserStore = mUserStore;
    }

    public void register() {
        mApiService.register();
    }
}

public class MainActivity extends AppCompatActivity {

    @Inject
    UserManager userManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        DaggerUserComponet.builder().userModule(new UserModule(this)).build().inject(this);
        userManager.register();
    }
}

4. Modular implementation
Okhttp or Retrofit is the most commonly used object in development right now, but it is obviously unnecessary to instantiate an OkhttpClient or Retrofit object for each request, so we need to unify a module to specifically generate the object.Here is an example of Okhttp:
First you need to define this Http module separately:

@Module
public class HttpModule {
    @Provides
    public OkHttpClient mOkhttpClient() {
        return new OkHttpClient().newBuilder().build();
    }
}


public class ApiService {

    OkHttpClient mOkHttpClient;
    public static final MediaType JSON = MediaType.parse("application/json;charset=utf-8");

    public ApiService(OkHttpClient okHttpClient) {
        Log.e("ApiService", "constructorApiService");
        this.mOkHttpClient = okHttpClient;
    }

    public void register() {
        Log.e("ApiService", "ApiService--register");
        RequestBody body = RequestBody.create(JSON, "");
        Request request = new Request.Builder().url("").post(body).build();
        mOkHttpClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {

            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {

            }
        });
    }



@Module(includes = {HttpModule.class})
public class UserModule {

    private Context mContext;

    public UserModule(Context context) {
        this.mContext = context;
    }

    //Provide Dependency

    @Provides
    public ApiService provideApiService(OkHttpClient client) {
        return new ApiService(client);
    }

    @Provides
    public UserStore providesUserStore() {
        return new UserStore(this.mContext);
    }

    @Provides
    public UserManager providesUserManager(ApiService apiService, UserStore userStore) {
        return new UserManager(apiService, userStore);
    }
}
}

In order to get an instance of ApiService, you need an okhttpClient instance, which is extracted in several ways:
Mode 1:
The above @Module(includes = {HttpModule.class}) is a way to associate HttpMoule as a module.
Mode 2: Create a new HttpComponet with UserComponet containing HttpComponet

@Component(modules = HttpModule.class)
public class HttpComponet {

}

//Is the bridge between Inject and Provides
//This sentence relates to modules
@Component(modules = {UserModule.class}, dependencies = HttpComponet.class)
public interface UserComponet {
    //This sentence relates to MainActivity
    void inject(MainActivity activity);

}

Mode 3: Write the associated Module directly on UserComponet

@Component(modules = {UserModule.class,HttpModule.class})
public interface UserComponet {
    //This sentence relates to MainActivity
    void inject(MainActivity activity);
}

The above three ways are three ways to achieve modularization, which extract the network request module separately.
5. Create and differentiate different instances
Suppose there are two identical ApiService instances in an Activity, one for dev and one for release, how to distinguish:
Mode 1: @Named

public class MainActivity extends AppCompatActivity {
    //Tell Dagger that this class needs to inject objects
    @Named("dev")
    @Inject
    ApiService mApiService; 

    @Named("release")
    @Inject
    ApiService mApiService; 

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        DaggerUserComponet.builder().userModule(new UserModule(this)).httpModule(new HttpModule())
                .build().inject(this);

    }
}


public class UserModule {

    private Context mContext;

    public UserModule(Context context) {
        this.mContext = context;
    }

    //Provide Dependency
    @Named("release")
    @Provides
    public ApiService provideApiServiceForRelease(OkHttpClient client) {
        return new ApiService(client);
    }

    @Named("dev")
    @Provides
    public ApiService provideApiServiceForDev(OkHttpClient client) {
        return new ApiService(client);
    }
}

Mode 2:
Create two custom notes:

@Qualifier
@Documented
@Retention(RUNTIME)
public @interface Release {

}

@Qualifier
@Documented
@Retention(RUNTIME)
public @interface Test {
}

public class MainActivity extends AppCompatActivity {
    //Tell Dagger that this class needs to inject objects
    @Test
    @Inject
    ApiService mApiService; 

    @Release
    @Inject
    ApiService mApiService; 

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        DaggerUserComponet.builder().userModule(new UserModule(this)).httpModule(new HttpModule())
                .build().inject(this);

    }
}


public class UserModule {

    private Context mContext;

    public UserModule(Context context) {
        this.mContext = context;
    }

    //Provide Dependency
    @Release
    @Provides
    public ApiService provideApiServiceForRelease(OkHttpClient client) {
        return new ApiService(client);
    }

    @Test
    @Provides
    public ApiService provideApiServiceForDev(OkHttpClient client) {
        return new ApiService(client);
    }
}

6,Singleton
When using Singleton, you need to add the @Singleton comment to both Module and Compont:

@Singleton
@Component(modules = {UserModule.class})
public interface UserComponet {
    //This sentence relates to MainActivity
    void inject(MainActivity activity);

}

@Provides
@Singleton
OkHttpClient providesOkhttp() {
   return new OkHttpClient().newBuilder().build();
}

7. Custom Scope
The Cope of a Module associated with the same Componet cannot be the same as the Cope of a dependent Componet, for example:

@Module
public class AppModule {
    @Singleton
    @Provides
    OkHttpClient providesOkhttp() {
        return new OkHttpClient().newBuilder().build();
    }

}

@Singleton
@Component(modules = AppModule.class)
public interface AppCompont {
    OkHttpClient okhttpClient();
}

@ActivityScope
@Component(modules = {UserModule.class},dependencies = AppCompont.class)
public interface UserComponet {
    //This sentence relates to MainActivity
    void inject(MainActivity activity);

}

@Scope
@Documented
@Retention(RUNTIME)
public @interface ActivityScope {

}

The code above customizes a Scope because the Scope of its own Componet in Dagger cannot be the same as the Scope of the dependent Componet, so you must customize a Scope again.
8. SubComponet, Lazyer and Provider
Note: SubComponent has both scopes of two different life cycles, SubComponent has both a Scope owned by the parent Component and its own scope, and SubCompnent has a smaller Scope than Component.

@ActivityScope
@Subcomponent(modules = UtilModule.class)
public interface CComponet {
    void inject(MainActivity activity);
}

@Singleton
@Component(modules = AppModule.class)
public interface FComponet {
    CComponet getChildComponet();
}

@Module
public class UtilModule {

    @Provides
    @ActivityScope
    public Gson providesGson(OkHttpClient okHttpClient){
        return new Gson();
    }
}

@Module
public class AppModule {
    @Singleton
    @Provides
    OkHttpClient providesOkhttp() {
        return new OkHttpClient().newBuilder().build();
    }
}

Lazyer and Provider:

    public class Container{
        @Inject
        Lazy<User> lazyUser;//Injecting Lazy elements
        @Inject
        Provider<User> providerUser;//Inject Provider Element
        public void init(){
            DaggerFComponet.create().getChildComponet().inject(MainActivity.this);
            User user1 = lazyUser.get();
            //Create user at this time and get the same User object each time you call get
            User user2 = providerUser.get();
            //use2 is created at this time, and each subsequent call to get forces the Module's Provides method to be called again, depending on the implementation of the Provides method
            //It may or may not return the same object as user2.
        }
    }

9. Points (Notes):

Here are some summaries of my study of Dagger2, some of which may have some problems. I welcome you to point out that we are making progress together.

Topics: network Gradle JSON Android