• Android注解使用之Dagger2实现项目依赖关系解耦


    前言:

       最近牵头发起公司app的重构工作,如何通过重构让项目的耦合降低、开发效率提高,一直是我努力的方向,今天来学习一下一个注解框架Dagger2,然后看看如何使用它来降低项目的耦合。    

    Dagger2

        一句话:一款快速的注解框架,应用于Android、Java,由 Google 开发和维护,是 Square 的 Dagger 项目的分支。

        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采用的就是注解注入的方式,然后编译自动生成目标代码的方式实现宿主与被依赖者之间的关系。

    Dagger2在Android的使用方式及简单说明

    在Android中的使用方式很简单:只需在Module的build.gradle中添加一下配置

    dependencies {
      compile 'com.google.dagger:dagger:2.x'
      annotationProcessor 'com.google.dagger:dagger-compiler:2.x'
    }

     Dagger2 annotation讲解

    • @Module 修饰的类专门用来提供依赖

    • @Provides 修饰的方法用在Module类里

    • @Inject  修饰需要依赖的地方(可以是构造方法、field或者一般的方法)

    • @Component 连接@Module和注入的桥梁

    Dagger2举例说明

     以项目中实际场景缓存管理为例,来体验一下解耦效果。设计遵循单一职责原则。

     1.首先定义缓存类和多任务类。并且在其构造函数上添加@Inject注解

    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();
        }
    }
    复制代码

    2.使用@Module分别定义LCacheModule、LExecutorModule类来提供相关依赖

    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);
        }
    }
    复制代码

    3.使用@Component 用来将@Inject和@Module关联起来,新建LCacheComponent类

    复制代码
    @Component(modules = {LCacheModule.class,LExecutorModule.class})
    @Singleton
    public interface LCacheComponent {
    
        LCache lCache();   // app缓存
    
        LExecutor lExecutor();  // app多任务线程池
    
        void inject(LCacheManager lCacheManager);
    }
    复制代码

    4.在宿主中注入想要依赖的对象

    复制代码
    /**
    * 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);
    }
    });
    }
    }
    复制代码

    5.使用场景调用及简单解说

    LCacheManager.getInstance().saveCache("key","who is lcj ?");

    看下打印结果:

    通过Dagger2的方式刚开始可能会觉得突然间一个简单的事情,变得复杂了,其实没有,通过Dagger2很好的处理好了依赖关系,具体说明,比如我们缓存LCache需要添加一个最大缓存个数变化,如果按照之前的方式,我们首先需要对LCache进行修改,比如修改构造函数增加maxCacheSize,然后必须对LCacheManager进行修改,现在通过Dagger2的方式的话,我们只需修改LCacheModule就可以了,LCache实例化和相关参数和LCacheManager之间并没有太大的依赖关系。

    6.关于@Module提供多个同类型@Provides

     基于上面的缓存处理需求,我们需要实现读写分别使用不同的多任务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来降低项目中耦合。

  • 相关阅读:
    CVI中调用数据库的几点..
    LabView、CVI、MeasurementStudio三者之间的区别
    cad工具快速选择特性里面是空的解决方法
    SDK和API的直接区别
    一角法与三角法视图的区别
    CentOS7桥接模式上不了外网的配置
    Ubuntu14.04或16.04下普通用户的root权限获得
    Eplan创建符号详细解说
    Eplan创建符号
    变量,浮点数,常量
  • 原文地址:https://www.cnblogs.com/wuyuxin/p/6993779.html
Copyright © 2020-2023  润新知