Android Dagger依赖注入框架浅析
今天接触了Dagger这套android的依赖注入框架(DI框架)。感觉跟Spring 的IOC差点儿相同吧。这个框架它的优点是它没有採用反射技术(Spring是用反射的),而是用预编译技术。因为基于反射的DI非常地耗用资源(空间,时间)
因为如今开发都是用Android Studio了,所以我这里大概讲下配置Dagger框架的开发环境。须要怎么做。
(因为Android Studio中用Gradle。所以跟传统我们用Eclipse配置的话。直接导入jar包,有点不一样。
)
在開始看我的博文前。希望大家有时间能够自己看下Dagger官网的文档:http://square.github.io/dagger/
相应的中文翻译:http://fanxu.me/post/2013-07-18#main(主要就是这篇文章翻译得非常具体,就是太长了,我主要对这文章进行了自己的理解还有自己的浅析哈,所以假设你时间充裕,而且足够耐心。能够先看这个。)
Dagger是构建在Android annotations的基础上的。假设你还不知道关于Android annotations,能够先看看我之前的一篇文章。
Android annotations浅析:http://blog.csdn.net/ljphhj/article/details/37601173
一、Android Studio 配置 Dagger 开源框架环境
1、建立一个module
2、在module中会有build.gradle的文件
3、在文件里的下列位置增加两行红色字体
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:19.+'
compile 'com.squareup.dagger:dagger:1.2.+'
provided 'com.squareup.dagger:dagger-compiler:1.2.+'
}
二、Dagger浅析
1、不论什么新生事物的产生都有其特定的历史背景,为什么会出现Dagger框架呢?
Dagger框架为了简化你的代码,让你把你的注意力转移到真正须要关注的类上,比方:
我们常说要通过工厂类来创建出产品类对象。
可是,往往我们更重视的是产品类,而并非工厂类,更不是它的生产过程。
2、Dagger框架怎么用?
Dagger框架图:
首先我要用大白话的形式让大家明白几个概念:
(以下类设计未必合理。仅仅为了让大家更好地理解)
1.@Inject注入对象
public class ClassRoom{
@Inject Student student1; //加上这个注解@Inject。 表示当前类ClassRoom要注入这样一个类Student的对象
//可是竟然我前面说Dagger不是通过反射机制。而是预编译的技术,那么它要怎么知道Student类对象由谁提供出来呢?
}
2.因为注入对象没有一个提供对象的地方是不能够的,所以引出了@Provides注解, 和 @Module的概念
//因为@Provides要包括在@Module凝视的类中,所以仅仅要函数中出现了@Provides就必须要在类上面加上@Module注解
@Module
public class StudentModule{
//加上了@Provides的方法,Dagger会去识别它的返回类型,当发现它的返回类型是Student类,上面第一步的@Inject就会来调用它,完毕注入。
@Provides Student provideStudent(){
return new Student();
}
}
约定@Provides函数以provide作为前缀, @Module类以Module作为后缀。
假设以上的你都理解了,那么接下来的东东就好办了!!
3.当我们希望无论多少个地方注入Student这个类。我们仅仅希望拥有一份“Student”的实例对象(单例),那么我们能够用到注解@Singleton 加在 @Provides注解的后面就可以
@Provides @Singleton Student provideStudent(){
return new Student();
}
@Singleton 凝视对Dagger有效, 也仅仅在一个ObjectGraph中生效。 若是有多个ObjectGraph。 则有多个相应的@Singleton对象。
4.延迟注入 Lazy :(即:懒载入, 等到调用的时候才注入)
public class ClassRoom{
@Inject Lazy<Student> lazyStudent;
public void study(){
lazyStudent.get();//这样就能得到一个Student对象
}
}
5.提供者注入 Provider
有些情况下, 你须要多个对象实例。 而不是仅仅注入一个对象实例。这时你能够利用Providerpublic class ClassRoom{
@Inject Provider<Student> providerStudent;
public void study(){
providerStudent.get(); //得到对象1
providerStudent.get(); //得到对象2
//对象1 和 对象2 是两个不同的对象.
}
}
6.限定符注解 @Qualifier : 个人认为有点像 “Web中自己定义标签”的感觉,也有点像 "C语言里宏定义" 的样子。
有些时候,单纯类型(指这些主要的@Inject....等等)是不能够满足指定依赖的需求的。
在这种情况下,我们能够增加限定符凝视. 这种凝视本身有一个@Qualifier凝视。
以下是javax.inject中@Named的声明代码:
@Qualifier @Retention(RUNTIME) public @interface Named { String value() default ""; }
这样写完之后,我们就新拥有了一个注解@Named, 来帮助我们限定我们想提供的类对象,还有我们获得的类对象的实例。
如:
public class ClassRoom{
@Inject @Named("胖虎") Student pangHu;
@Inject @Named("李四") Student liSi;
//这样就限定了所要获取的Student类对象实例的性质了
}
那么我们之前说必须要有提供 类对象实例的方法,那么如今有限定符的话,我们要怎么来写这种方法呢?
@Module
public class StudentModule{
@Provides @Named("胖虎") Student providePangHuStudent(){
return new Student("胖虎"); //假设该Student类有个这种构造函数
}
@Provides @Named("李四") Student provideLiSiStudent(){
return new Student("李四");
}
}
依赖关系也能够同一时候有多重限定符凝视。
7.静态注入(staticInjections):建议慎重使用这个特性。 因为静态依赖注入非常难測试和复用。
Dagger能够注入静态变量。 拥有@Inject静态变量的类必须在@Module的staticInjections中明白说明。 能够使用ObjectGraph.injectStatics()注入静态变量: 8.编译时有效性的检查(这个非常重要) Dagger包括一个annotation 处理器, 这个处理器检查module和注入的有效性。处理器非常严格, 若是有不论什么绑定是无效或者不完整的, 将引发编译错误。 那么应该遵循什么样的规则呢?我举官网上的一个样例来跟大家分析下哈。 @Module Heater provideHeater(Executor executor) { 分析:因为我们知道。provideHeater是在有别的类@Inject了Heater类(或子类)的时候。会来调用这种方法。也就是说这个全然是由Dagger框架来调用的,而并非传统意义上我们自己调用这个函数。 那么问题应该就非常明显了吧??? 这个參数。应该要由我们来提供吧?肯定有一个@Module里面有一个方法是叫做 provideExecutor(), 可是我们这边也不能用@Inject来注解这个參数。那要怎么让我们这个当前类。知道要去找那个方法拿这个參数呢? 有两种方法: 1.把这个provideExecutor()方法放入到我们这个类中来,这样子它就能够找到这种方法并进行注入这个參数对象了。 2.在@Module中增加一个參数 complete=false, 标记说明该Module为不完整的Module。 因为不完整的Module同意缺少对象实例 @Module(complete=false) Heater provideHeater(Executor executor) { 另一个比較难理解的就是关于injects了 是这种,假设在@Module中增加參数injects (即所谓的:注入对象列表绑定)。 若是这个Module提供的对象绑定。 可能被injects列表中以外的类使用, 能够将改Module标记为library, 以避免出错。 如: @Module( injects = ClassRoom.class, library = true ) public class StudentModule{ @Provides Student provideStudent(){ return new Student(); } @Provides Others provideOthers(){ return new Others; } } 分析:因为ClassRoom中仅仅用到了一个Student的类,而injects列表中也仅仅写了ClassRoom.class, 这种话。这个类提供的其它方法有可能被除了ClassRoom之外的类所用,那么避免报错就要在@Module加上參数library=true 9.全部@Provides要放在一个@Module中 因为Dagger规定全部@Provides要放在一个@Module中。所以我们要么能够在一个Module中用includes參数把其它的Module类包括进来 或者比較建议的是:再创建一个空的Module类,把全部的Module都包括到这个Module中来。 10.Module重载(引用官网的样例): 了解下就能够了 若对同一个依赖关系有多个@Provides函数, Dagger 将会报错。 但在有些情况下,是有必要替换production代码的, 比方測试和开发。 在@Module中能够使用overrides =true 。 重载其绑定关系。 这种重载方式也非常适合程序的小型变动, 比如付费版。免费版。@Module(
staticInjections = LegacyCoffeeUtils.class
)
class LegacyModule {
}
ObjectGraph objectGraph = ObjectGraph.create(new LegacyModule());
objectGraph.injectStatics();
public class DripCoffeeModule {
@Provides
return new CpuHeater(executor);
}
}
public class DripCoffeeModule {
@Provides
return new CpuHeater(executor);
}
}@Module(
includes = {
StudentModule.class,
ExecutorModule.class
}
)
public class AppModules {
}
以下这个JUnit測试利用Mockito, 重载了DripCoffeeModule的Heater绑定关系。这个Mock对象将inject到CoffeeMake中。public class CoffeeMakerTest {
@Inject CoffeeMaker coffeeMaker;
@Inject Heater heater;
@Before public void setUp() {
ObjectGraph.create(new TestModule()).inject(this);
}
@Module(
includes = DripCoffeeModule.class,
injects = CoffeeMakerTest.class,
overrides = true
)
static class TestModule {
@Provides @Singleton Heater provideHeater() {
return Mockito.mock(Heater.class);
}
}
@Test public void testHeaterIsTurnedOnAndThenOff() {
Mockito.when(heater.isHot()).thenReturn(true);
coffeeMaker.brew();
Mockito.verify(heater, Mockito.times(1)).on();
Mockito.verify(heater, Mockito.times(1)).off();
}
}
相关文章
- 加密狗android,Android系统加密狗的设计与实现
- android okio使用方法,Android 开源框架 Okio 原理剖析「建议收藏」
- android 读取本地数据库db文件(Android sqlite)
- android autosize原理,Android屏幕适配头条:autosize的原理
- strictmode android,(十三)Android 性能优化 StrictMode
- android activity singletask,Android Activity启动模式之singleTask实例详解
- Android FFmpeg系列06--音频可视化
- android之实现打开相册、拍照录像、播放视频、保存图片到系统相册指定位置、图片压缩[通俗易懂]
- Android angle_android 界面悬停
- Android触摸事件_android设置按钮点击事件
- android activitymanager 系统api_Android view
- android studio输出文字_androiditem长按删除
- Android 数据库加密 android-database-sqlcipher 开源版本编译过程
- 【Android 组件化】路由组件 ( 页面跳转参数依赖注入 )
- 【IOC 控制反转】Android 事件依赖注入 ( 事件依赖注入具体的操作细节 | 获取要注入事件的 View 对象 | 通过反射获取 View 组件的事件设置方法 )
- 【IOC 控制反转】Android 事件依赖注入 ( 事件依赖注入具体的操作细节 | 创建 事件监听器 对应的 动态代理 | 动态代理的数据准备 | 创建调用处理程序 | 创建动态代理实例对象 )
- 【Android 逆向】使用 DB Browser 查看并修改 SQLite 数据库 ( 下载 DB Browser 安装包 | 安装 DB Browser 工具 )
- 【Android 逆向】Android 进程注入工具开发 ( EIP 寄存器指向 dlopen 函数 | ESP 寄存器指向栈内存 | 调试程序收回目标进程控制权 )
- 【Android 逆向】Android 进程注入工具开发 ( SO 进程注入环境及 root 权限获取 | 进程注入时序分析 )
- 【Android 逆向】Android 进程注入工具开发 ( 注入代码分析 | 获取 远程 目标进程 中的 /system/lib/libc.so 动态库中的 mmap 函数地址 )
- 【Android UI】贝塞尔曲线 ④ ( 使用 android.graphics.Path 提供的 cubicTo 方法绘制三阶贝塞尔曲线示例 )
- 【Android 逆向】ART 函数抽取加壳 ④ ( 对 libc.so#execve 函数进行内联 HOOK 操作 )
- Android Studio 导入项目 出现安装Error:Cause: failed to find target with hash string ‘android-23’ 等错误详解手机开发
- Android Auto亮相:全语音控制
- 使用Android实现连接MySQL数据库:实现快速数据交互与管理(android连接mysql数据库)
- 提升Android应用视觉吸引效果的10个UI设计技巧
- Android编程下字库的使用及注意事项
- Android简单的利用MediaRecorder进行录音的实例代码
- 浅析android中的线程封装
- Android上使用ZXing识别条形码与二维码的方法