zoukankan      html  css  js  c++  java
  • 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来降低项目中耦合。

  • 相关阅读:
    OSI安全体系结构
    PHP 二维数组根据相同的值进行合并
    Java实现 LeetCode 17 电话号码的字母组合
    Java实现 LeetCode 16 最接近的三数之和
    Java实现 LeetCode 16 最接近的三数之和
    Java实现 LeetCode 16 最接近的三数之和
    Java实现 LeetCode 15 三数之和
    Java实现 LeetCode 15 三数之和
    Java实现 LeetCode 15 三数之和
    Java实现 LeetCode 14 最长公共前缀
  • 原文地址:https://www.cnblogs.com/whoislcj/p/6626540.html
Copyright © 2011-2022 走看看