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来降低项目中耦合。
Android注解使用之Dagger2实现项目依赖关系解耦的更多相关文章
- android编译/反编译常用工具及项目依赖关系
项目依赖关系 apktool:依赖smali/baksmali,XML部分 AXMLPrinter2 JEB:dx 工具依赖 AOSP , 反编译dex 依赖 apktool dex2jar:依赖 A ...
- Android Gradle Plugin指南(三)——依赖关系、android库和多项目配置
原文地址:http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Dependencies-Android-Librari ...
- Android注解框架实战-ButterKnife
文章大纲 Android注解框架介绍 ButterKnife实战 项目源码下载 一.框架介绍 为什么要用注解框架? 在Android开发过程中,我们经常性地需要操作组件,操作方法有findVie ...
- Bean 注解(Annotation)配置(3)- 依赖注入配置
Spring 系列教程 Spring 框架介绍 Spring 框架模块 Spring开发环境搭建(Eclipse) 创建一个简单的Spring应用 Spring 控制反转容器(Inversion of ...
- Python之用虚拟环境隔离项目,并重建依赖关系
下面将以安装django和mysqlclient介绍如何用虚拟环境隔离项目,并重建依赖关系.操作系统:windows 10:python版本:python3.7 1. 安装python虚拟环境 (1) ...
- Chromium之工程依赖关系.
Chromium各版本可能有差异,我的版本是chromium.r197479,2013/08前后下载的source code. Visual Studio Ultimate版本有工具可以自动生成项目依 ...
- 【WPF学习笔记】之WPF基础:依赖关系属性和通知
这些天来,对象似乎已经忙得晕头转向了.每个人都希望它们做这做那.Windows® Presentation Foundation (WPF) 应用程序中的典型对象会接到各种各样不同的请求:有要求绑定到 ...
- Java Android 注解(Annotation) 及几个常用开源项目注解原理简析
不少开源库(ButterKnife.Retrofit.ActiveAndroid等等)都用到了注解的方式来简化代码提高开发效率. 本文简单介绍下 Annotation 示例.概念及作用.分类.自定义. ...
- Android Studio Jar、so、library项目依赖
Eclipse跟AS的不同 从Eclipse到AS不要带着在Eclipse中的主观色彩去在AS中使用,从项目的构成到构建是不同的,下面列举在Eclipse和AS中的一些概念的区别: WorkSpace ...
随机推荐
- angular 输入框实现自定义验证
此插件使用angular.js.JQuery实现.(jQuery的引入需在angular 之前) 用户可以 在输入框输入数据后验证 必填项.整数型.浮点型验证. 如果在form 里面的输入框验证,可以 ...
- 在华为oj的两个月
一次偶然的机会,我接触到华为oj平台(http://career-oj.huawei.com/exam/camLogin.jsp),当时的心情很是兴奋,于是立马注册开通,然后迫不及待地上去做题.刚开始 ...
- 以setTimeout来聊聊Event Loop
平时的工作中,也许你会经常用到setTimeout这个方法,可是你真的了解setTimeout吗?本文想通过总结setTimeout的用法,顺便来探索javascript里面的事件执行机制. setT ...
- SQL SERVER的事务日志
1 基本介绍 每个数据库都具有事务日志,用于记录所有事物以及每个事物对数据库所作的操作. 日志的记录形式需要根据数据库的恢复模式来确定,数据库恢复模式有三种: 完整模式,完全记录事物日志,需要定期进行 ...
- 极光推送助推视频App,打造最活跃手机新媒体平台
移动应用能够帮助吸引更多的新用户,增加用户互动和对话.但你得让用户想起你,如何在一部手机上数十个App中脱颖而出,是考验App运营的关键之处.为了打造一个成功的App,开发者需要着眼长远,不应局限于其 ...
- maven lean install 的时候出错 Failed to clean project
问题解决1 : 这种情况是属于 本地有多个 java 线程,关掉其中不用的,或者 都关闭就可以了. 问题解决 2 : Caused by: org.springframework.beans.f ...
- ForEach 循环
在C 标签里面 有个foreach 标签,这个标签是专门来做循环的标签: <c:forEach items="${wekList}" var="list" ...
- 利用smarty模板(登录、有关信息操作等功能)
smarty模板前提是:前端和后端是分开的,所以肯定会有很多的后台页面,php页面和html页面是分开存储的. (1)登录页面的编写也是分两个页面(后台和前端) 1.首先是后台的php页面,很简单只要 ...
- Troubleshooting OpenStack 瘫痪 - 每天5分钟玩转 OpenStack(160)
这是 OpenStack 实施经验分享系列的第 10 篇.是软件就会有 bug,OpenStack 也不例外,只要用它就一定会遇到故障.Troubleshooting(故障排除)是运维 OpenSta ...
- 删除bin后,Eclipse重新编译项目
今天做"用java.util.Properties类读写配置文件"Demo时,在编译项目时由于配置资源文件一起写入bin了.而Demo修改了配置文件,从新运行时配置文件不再更新,于 ...