最近牵头发起公司 app 的重构工作,如何通过重构让项目的耦合降低、开发效率提高,一直是我努力的方向,今天来学习一下一个注解框架 Dagger2,然后看看如何使用它来降低项目的耦合。
一句话:一款快速的注解框架,应用于 Android、Java,由 Google 开发和维护,是 的 项目的分支。
gitHub:https://github.com/google/dagger
Dagger2 采用依赖注入方式,依赖注入是一种面向对象的编程模式,它的出现是为了降低耦合性,所谓耦合就是类之间依赖关系,所谓降低耦合就是降低类和类之间依赖关系。
Java 的面向对象编程特性,通常会在一个 Java 对象中引用另一个 Java 对象,举例说明一下:
- public class ClassA {
- private ClassB classB;
- public ClassA(){
- classB =new ClassB();
- }
- public void doSomething(){
- classB.doSomething();
- }
- }
通过上面的例子可以看出,ClassA 需要借助 ClassB 才能完成一些特定操作,但是我们在 ClassA 直接实例化了 ClassB,这样耦合就产生了,第一违背了单一职责原则,ClassB 的实例化应该由自己完成,不应该由 ClassA 来完成,第二违背了开闭原则,一旦 ClassB 的构造函数产生变化,就需要修改 ClassA 的构造函数。
通过依赖注入降低这种耦合关系:
1. 通过构造参数传参的方式
- public class ClassA {
- private ClassB classB;
- public ClassA(ClassB classB){
- this.classB =classB;
- }
- public void doSomething(){
- classB.doSomething();
- }
- }
2. 通过 set 方法的方式
- public class ClassA {
- private ClassB classB;
- public ClassA(){
- }
- public void setClassB(ClassB classB) {
- this.classB = classB;
- }
- public void doSomething(){
- classB.doSomething();
- }
- }
3. 通过接口注入的方式
- interface ClassBInterface {
- void setB(ClassB classB);
- }
- public class ClassA implements ClassBInterface {
- private ClassB classB;
- public ClassA() {
- }
- @Override
- public void setB(ClassB classB) {
- this.classB = classB;
- }
- public void doSomething() {
- classB.doSomething();
- }
- }
4. 通过注解注入
- public class ClassA {
- @Inject
- ClassB classB;
- public ClassA() {
- }
- public void doSomething() {
- classB.doSomething();
- }
- }
Dagger2 采用的就是注解注入的方式,然后编译自动生成目标代码的方式实现宿主与被依赖者之间的关系。
在 Android 中的使用方式很简单:只需在 Module 的 build.gradle 中添加一下配置
- dependencies {
- compile 'com.google.dagger:dagger:2.x'
- annotationProcessor 'com.google.dagger:dagger-compiler:2.x'
- }
Dagger2 annotation 讲解
以项目中实际场景缓存管理为例,来体验一下解耦效果。设计遵循单一职责原则。
LCache 类
- /**
- * Created by lichaojun on 2017/3/30.
- * 处理缓存
- */
- public class LCache {
- private static final String DEFAULT_CACHE_NAME="LCache";//默认缓存名字
- private static final int DEFAULT_MAX_CACHE_SIZE=1024;//默认缓存名字
- private String cacheName=DEFAULT_CACHE_NAME;//缓存名字
- private int maxCacheSize=DEFAULT_MAX_CACHE_SIZE;
- public LCache (){
- }
- @Inject
- public LCache(String cacheName,int maxCacheSize){
- this.cacheName=cacheName;
- this.maxCacheSize=maxCacheSize;
- }
- public void saveCache(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 void readCache(String key){
- Log.e(LCacheManager.TAG,"readCache: key: = "+key);
- }
- }
LExecutor 类
- public class LExecutor {
- private static final int DEFAULT_CPU_CORE = Runtime.getRuntime().availableProcessors();//默认线程池维护线程的最少数量
- private int coreSize = DEFAULT_CPU_CORE;//线程池维护线程的最少数量
- @Inject
- public LExecutor(int coreSize) {
- this.coreSize = coreSize;
- }
- public void runTask(Runnable runnable) {
- if (runnable == null) {
- return;
- }
- Log.e(LCacheManager.TAG,"coreSize: = "+coreSize);
- Log.e(LCacheManager.TAG, "runTask");
- runnable.run();
- }
- }
LCacheModule 类
- @Module
- public class LCacheModule {
- /**
- * 提供缓存对象
- * @return 返回缓存对象
- */
- @Provides
- @Singleton
- LCache provideLCache() {
- return new LCache("lcj",500);
- }
- }
LExecutorModule 类
- @Module
- public class LExecutorModule {
- /**
- * 提供app 多任务最少维护线程个数
- * @return 返回多任务最少维护线程个数
- */
- @Provides
- @Singleton
- LExecutor provideLExecutor() {
- return new LExecutor(10);
- }
- }
- @Component(modules = {LCacheModule.class,LExecutorModule.class})
- @Singleton
- public interface LCacheComponent {
- LCache lCache(); // app缓存
- LExecutor lExecutor(); // app多任务线程池
- void inject(LCacheManager lCacheManager);
- }
- /**
- * Created by lichaojun on 2017/3/30.
- * 缓存处理管理
- */
- 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);
- }
- });
- }
- }
- LCacheManager.getInstance().saveCache("key","who is lcj ?");
看下打印结果:
通过 Dagger2 的方式刚开始可能会觉得突然间一个简单的事情,变得复杂了,其实没有,通过 Dagger2 很好的处理好了依赖关系,具体说明,比如我们缓存 LCache 需要添加一个最大缓存个数变化,如果按照之前的方式,我们首先需要对 LCache 进行修改,比如修改构造函数增加 maxCacheSize,然后必须对 LCacheManager 进行修改,现在通过 Dagger2 的方式的话,我们只需修改 LCacheModule 就可以了,LCache 实例化和相关参数和 LCacheManager 之间并没有太大的依赖关系。
基于上面的缓存处理需求,我们需要实现读写分别使用不同的多任务 LExecutor,并且 LExecutor 的最小线程数为 5,我们会在 LCacheComponent 添加提供 writeLExecutor 函数,如下:
- @Component(modules = {LCacheModule.class,LExecutorModule.class})
- @Singleton
- public interface LCacheComponent {
- LCache lCache(); // app缓存
- LExecutor lExecutor(); // app多任务线程池
- LExecutor writeLExecutor(); // app 写缓存多任务线程池
- void inject(LCacheManager lCacheManager);
- }
在 LExecutorModule 中添加提供依赖初始化的 provideWriteLExecutor 函数。如下:
- @Module
- public class LExecutorModule {
- /**
- * 提供app 多任务最少维护线程个数
- * @return 返回多任务最少维护线程个数
- */
- @Provides
- @Singleton
- LExecutor provideLExecutor() {
- return new LExecutor(10);
- }
- /**
- * 提供app 多任务最少维护线程个数
- * @return 返回多任务最少维护线程个数
- */
- @Provides
- @Singleton
- LExecutor provideWriteLExecutor() {
- return new LExecutor(5);
- }
- }
然后写完之后 Rebuild 一下项目,以为万事大吉了,结果报了如下错误,
怎么办呢,难道 Dagger2 就这么不堪一击吗,当然不是解决这个问题很容易,使用 @Named 注解解决这个问题,我们只需要在 LCacheComponent 的 writeLExecutor() 和
LExecutorModule 的 provideWriteLExecutor() 函数上添加相同的 @Named("WriteLExecutor") 即可。
对于 Module 的 provide 函数也是可以传递参数的,不过需要在当前 Module 中需要提供相关的参数的函数。例如:LCacheModule 可以修改如下:
- @Module
- public class LCacheModule {
- /**
- * 提供缓存对象
- * @return 返回缓存对象
- */
- @Provides
- @Singleton
- LCache provideLCache( @Named("LCache")String name , @Named("LCache")int maxCacheSize) {
- return new LCache(name,maxCacheSize);
- }
- /**
- * 提供缓存对象
- * @return 返回缓存对象
- */
- @Provides
- @Singleton
- @Named("LCache")
- String provideLCacheName() {
- return "lcjCache";
- }
- /**
- * 提供缓存对象
- * @return 返回缓存对象
- */
- @Provides
- @Singleton
- @Named("LCache")
- int provideLCacheMaxSize() {
- return 600;
- }
- }
这里又使用了别名 @Name 也是因为为了避免 bound multiple times 错误导致编译失败,在编译的过程中 Dagger2 会自动去寻找相关参数进行绑定依赖关系,这点还是挺神奇的。
今天简单的写个例子对 Dagger2 有个初步的理解与认识,由于项目并没有采用 MVP 设计模式,准备逐步采用 Dagger2+MVP 来降低项目中耦合。
来源: http://www.cnblogs.com/whoislcj/p/6626540.html