Objective:
Recently led the launch of the company's application refactoring work, how to make the project by refactoring to reduce the coupling, development efficiency, has been the direction of my efforts, today to learn a note framework Dagger2, and then see how to use it to reduce the coupling of the project.
Dagger2
Bottom line: A quick annotation framework for Android, Java, developed and maintained by Google, is a branch of Square's Dagger project.
Github:https://github.com/google/dagger
DAGGER2 relies on injection, dependency injection is an object-oriented programming pattern, it is to reduce the coupling, so-called coupling is the dependency between classes, so-called reduction of coupling is to reduce the class and class dependency relationship.
Dependent relationships
Java's object-oriented programming features, which typically refer to another Java object in one Java object, illustrate:
Public class ClassA { private ClassB ClassB; Public ClassA () { =new ClassB (); } Public void dosomething () { classb.dosomething (); }}
As can be seen from the above example, ClassA need to use CLASSB to complete some specific operations, but we classa directly instantiate the CLASSB, so that the coupling produced, the first violation of the single principle of responsibility, CLASSB instantiation should be done by themselves, Should not be done by ClassA, the second violation of the opening and closing principle, once the classb of the constructor changes, you need to modify the ClassA constructor.
Reduce this coupling relationship through dependency injection:
1. How to pass parameters by constructing parameter
Public class ClassA { private ClassB ClassB; Public ClassA (ClassB ClassB) { this. ClassB =ClassB; } Public void dosomething () { classb.dosomething (); }}
2. By the Set method
Public class ClassA { private ClassB ClassB; Public ClassA () { } publicvoid setclassb (ClassB ClassB) { this. ClassB = ClassB; } Public void dosomething () { classb.dosomething (); }}
3. How to inject through the interface
InterfaceClassbinterface {voidSetb (ClassB ClassB);} Public classClassAImplementsClassbinterface {PrivateClassB ClassB; PublicClassA () {} @Override Public voidSetb (ClassB ClassB) { This. ClassB =ClassB; } Public voiddosomething () {classb.dosomething (); }}
4. Through annotation injection
Public class ClassA { @Inject ClassB ClassB; Public ClassA () { } publicvoid dosomething () { classb.dosomething ();} }
Dagger2 uses the annotation injection method, and then compiles the way the object code is generated automatically to achieve the relationship between the host and the dependent person.
Dagger2 and simple instructions on how to use Android
The way to use Android is simple: Just add the configuration in the module's Build.gradle
Dependencies { ' com.google.dagger:dagger:2.x ' com.google.dagger:dagger-compiler:2.x ' }
Dagger2 annotation Explanation
@Module-decorated classes are designed to provide dependency
The method of @Provides modification is used in the module class.
Where @Inject modification is required (can be a construction method, field, or general method)
@Component connect the @module and inject the bridge
Dagger2 Illustrative examples
Take the actual scenario cache management in the project as an example to try out the decoupling effect. The design follows a single responsibility principle.
1. First define cache classes and multitasking classes. and add @inject annotations on its constructor
Lcache class
/*** Created by Lichaojun on 2017/3/30. * Processing Cache*/ Public classLcache {Private Static FinalString default_cache_name= "Lcache";//Default cache name Private Static Final intdefault_max_cache_size=1024;//Default cache name PrivateString Cachename=default_cache_name;//Cache Name Private intMaxcachesize=default_max_cache_size; PublicLcache () {} @Inject PublicLcache (String CacheName,intmaxcachesize) { This. cachename=CacheName; This. maxcachesize=maxcachesize; } Public voidSavecache (string key, String value) {LOG.E (Lcachemanager.tag,"CacheName: =" +cachename); LOG.E (Lcachemanager.tag,"Maxcachesize: =" +maxcachesize); LOG.E (Lcachemanager.tag,"Savecache:key =" +key + "value =" +value); } Public voidReadcache (String key) {LOG.E (Lcachemanager.tag,"Readcache:key: =" +key); }}
Lexecutor class
Public classLexecutor {Private Static Final intDefault_cpu_core = Runtime.getruntime (). Availableprocessors ();//The default thread pool maintains a minimum number of threads Private intCoresize = Default_cpu_core;//The thread pool maintains a minimum number of threads@Inject PublicLexecutor (intcoresize) { This. coresize =coresize; } Public voidRunTask (Runnable Runnable) {if(runnable = =NULL) { return; } log.e (Lcachemanager.tag,"Coresize: =" +coresize); LOG.E (Lcachemanager.tag,"RunTask"); Runnable.run (); }}
2. Use @module to define Lcachemodule, Lexecutormodule classes to provide related dependencies
Lcachemodule class
@Module Public class lcachemodule { /** * Provides cached objects @return return cached objects * / @Provides @Singleton lcache providelcache () { returnNew Lcache ("LCJ"); }}
Lexecutormodule class
@Module Public class lexecutormodule { /** * Provides the minimum number of maintenance threads for app multitasking @return Returns the minimum number of maintenance threads for multitasking * /@Provides @Singleton lexecutor providelexecutor () { return New Lexecutor (ten); }}
3. Use @component to associate @inject with @module and create a new Lcachecomponent class
@Component (modules = {Lcachemodule. Class, Lexecutormodule. class }) @Singletonpublicinterface lcachecomponent { lcache lcache (); // App Cache lexecutor lexecutor (); // app multi-task thread pool void inject (Lcachemanager lcachemanager);}
4. Inject the object you want to rely on in the host
/**
* Created by Lichaojun on 2017/3/30.
* Cache Processing Management
*/
public class Lcachemanager {
public static final String tag=lcachemanager.class.getsimplename ();
Private Lcachecomponent cachecomponent;
private Static Class Singletonholder {
private static Lcachemanager instance = new Lcachemanager ();
}
Private Lcachemanager () {
Cachecomponent = Daggerlcachecomponent.builder (). Lcachemodule (New Lcachemodule ()). build ();
Cachecomponent.inject (this);
}
public static Lcachemanager getinstance () {
return singletonholder.instance;
}
public void Savecache (final string key, final string value) {
Cachecomponent.lexecutor (). RunTask (New Runnable () {
@Override
public void Run () {
Cachecomponent.lcache (). Savecache (Key,value);
}
});
}
public void Readcache (final String key) {
Cachecomponent.lexecutor (). RunTask (New Runnable () {
@Override
public void Run () {
Cachecomponent.lcache (). Readcache (key);
}
});
}
}
5. Using scene calls and simple explanations
Lcachemanager.getinstance (). Savecache ("Key", "Who is LCJ?");
Look at the print results:
Through the Dagger2 way at first may feel suddenly a simple thing, become complex, actually did not, through Dagger2 very good to handle the dependency, specific, such as our cache lcache need to add a maximum number of cache changes, if the previous way, We first need to modify the Lcache, such as modifying the constructor to add Maxcachesize, and then have to modify the Lcachemanager, now through the Dagger2 way, we only need to modify the lcachemodule on it, There is not much dependency between lcache instantiation and correlation parameters and Lcachemanager.
6. About @module offers a number of similar types of @provides
Based on the cache processing requirements above, we need to implement read/write using different multitasking lexecutor, and the minimum number of threads for Lexecutor is 5, we will add the Writelexecutor function in Lcachecomponent, as follows:
@Component (modules = {Lcachemodule. Class, Lexecutormodule. class }) @Singletonpublicinterface lcachecomponent { lcache lcache (); // App Cache lexecutor lexecutor (); // app multi-task thread pool lexecutor writelexecutor (); // App write cache multi-task thread pool void inject (Lcachemanager lcachemanager);}
Add the Providewritelexecutor function that provides dependency initialization in Lexecutormodule. As follows:
@Module Public classLexecutormodule {/*** Minimum number of maintenance threads for app multitasking *@returnreturns the minimum number of maintenance threads for multiple tasks*/@Provides @Singleton lexecutor providelexecutor () {return NewLexecutor (10); } /*** Minimum number of maintenance threads for app multitasking *@returnreturns the minimum number of maintenance threads for multiple tasks*/@Provides @Singleton lexecutor providewritelexecutor () {return NewLexecutor (5); }}
And then rebuild after writing the project, thought that everything is done, the results reported the following error,
What to do, is it Dagger2 so vulnerable, of course, not to solve the problem is very easy to use @named annotations to solve this problem, we only need to lcachecomponent writelexecutor () and
Add the same @named ("Writelexecutor") on Lexecutormodule's Providewritelexecutor () function.
The Provide function for the module is also a function that can pass parameters, but needs to provide the relevant parameters in the current module. For example: Lcachemodule can be modified as follows:
@Module Public classLcachemodule {/*** Provide cached objects *@returnReturning cached Objects*/@Provides @Singleton Lcache providelcache (@Named ("Lcache") String name, @Named ("Lcache")intmaxcachesize) { return NewLcache (name,maxcachesize); } /*** Provide cached objects *@returnReturning cached Objects*/@Provides @Singleton @Named ("Lcache") String Providelcachename () {return"Lcjcache"; } /*** Provide cached objects *@returnReturning cached Objects*/@Provides @Singleton @Named ("Lcache") intprovidelcachemaxsize () {return600; }}
The alias @name is also used here because in order to avoid the bound multiple times error caused the compilation failure, in the process of compiling Dagger2 automatically to find the relevant parameters for binding dependencies, this is quite magical.
Summarize:
Today, a simple example of the Dagger2 has a preliminary understanding and understanding, because the project has not adopted the MVP design model, is prepared to gradually adopt the DAGGER2+MVP to reduce the coupling in the project.
Android annotations Use the Dagger2 to decouple project dependencies