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来降低项目中耦合。

时间: 2024-10-11 21:40:52

Android注解使用之Dagger2实现项目依赖关系解耦的相关文章

Android Gradle Pluin指南(三)——依赖关系、android库和多项目配置

原文地址:http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Dependencies-Android-Libraries-and-Multi-project-setup 4.Dependencies,Android Libraries and Multi-project setup(依赖关系,Android库和多项目设置) Gradle项目可以依赖于其它组件.这些组件可以是外部二进制包,或者是其它的Gradle项

Android Gradle Plugin指南(三)——依赖关系、android库和多项目配置

原文地址:http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Dependencies-Android-Libraries-and-Multi-project-setup 4.Dependencies.Android Libraries and Multi-project setup(依赖关系,Android库和多项目设置) Gradle项目能够依赖于其他组件.这些组件能够是外部二进制包,或者是其他的Gradle项

Android error: install libsdl-dev 未满足的依赖关系

Android编译时需要用到 libsdl-dev 库: 安装时遇到错误: sudo apt-get install libsdl-dev 正在读取软件包列表... 完成 正在分析软件包的依赖关系树 正在读取状态信息... 完成 注意,选取 libsdl1.2-dev 而非 libsdl-dev 有一些软件包无法被安装.如果您用的是 unstable 发行版,这也许是 因为系统无法达到您要求的状态造成的.该版本中可能会有一些您需要的软件 包尚未被创建或是它们已被从新到(Incoming)目录移出

【转载】Android Studio jar、so、library项目依赖,原文链接http://zhengxiaopeng.com/2014/12/13/Android-Studio-jar、so、library项目依赖/

前言 Android Studio(以下简称AS)在13年I/O大会后放出预览版到现在放出的正式版1.0(PS.今天又更新到1.0.1了)历时一年多了,虽然Google官方推出的Android开发者的IDE对我们Android DEV是很有吸引力的,但考虑到beta版还是太多问题所以自己主要还是把AS当做尝鲜为主,每放出一个较大更新就下载下来试试,感觉还是挺好的,渐渐用AS的人越来越多,Github上的项目也基本是AS的了,Google的sample也采用AS,所以使用Eclipse跟外界交流越

Java Android 注解(Annotation) 及几个常用开源项目注解原理简析

不少开源库(ButterKnife.Retrofit.ActiveAndroid等等)都用到了注解的方式来简化代码提高开发效率. 本文简单介绍下 Annotation 示例.概念及作用.分类.自定义.解析,并对几个 Android 开源库 Annotation 原理进行简析.PDF 版: Java Annotation.pdf, PPT 版:Java Annotation.pptx, Keynote 版:Java Annotation.key 完整版原文见:Java Android 注解(Ann

Android 查看项目依赖树的四种方式

Android 查看项目依赖树的四种方式: 方式一: ./gradlew 模块名:dependencies //查看单独模块的依赖 ./gradlew :app:dependencies --configuration compile //查看项目的编译依赖 方式二:使用Gradle Project 方式三:安装Android Studio插件 gradle view 方式四:如果你嫌在命令行窗口展示观看不友好,这里还有一种体验更好的方式. 输入下面命令行: ./gradlew build --

Android注解使用之通过annotationProcessor注解生成代码实现自己的ButterKnife框架

前言: Annotation注解在Android的开发中的使用越来越普遍,例如EventBus.ButterKnife.Dagger2等,之前使用注解的时候需要利用反射机制势必影响到运行效率及性能,直到后来android-apt的出现通过注解根据反射机制动态编译生成代码的方式来解决在运行时不再使用发射机制,不过随着android-apt的退出不再维护,我们今天利用Android studio的官方插件annotationProcessor来实现一下自己的ButterKnife UI注解框架. 需

android注解处理技术APT

APT(Annotation Processing Tool)是java的注解处理技术,它对源代码文件进行检测找出其中的Annotation,根据注解和注解处理器和相应的apt自动生成代码. Annotation处理器在处理Annotation时可以根据源文件中的Annotation生成额外的源文件和其它的文件(文件具体内容由Annotation处理器的编写者决定). 1.什么是APT? android-apt和annotationProcessor功能是一样的,都是apt的实现,前者比较早,后

Android Studio之同一窗口打开项目

Android Studio默认新打开的项目都是重新打开一个窗口,和原项目窗口同时存在,如果打开多个项目,则有很多窗口同时打开,怎么根据需要决定自己以何种方式打开呢? 1.设置打开新项目的方式 第一项是系统默认的,即每次打开新窗口 第二项表示,每次打开项目都在当前窗口 第三项表示,每次打开都会询问用户以前面两种哪种方式打开(推荐) 如果不希望提示,可以选择第二项 2.打开新的项目 如果之前打开过其他项目,则点击“File-Reopen Project”,后面会列出常用的项目名称,选择其中一个打开