4

Android Dagger依赖注入框架浅析

 3 years ago
source link: https://blog.csdn.net/ljphhj/article/details/37663071
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

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

有些情况下, 你需要多个对象实例, 而不是仅仅注入一个对象实例。这时你可以利用Provider实现, 每次调用Provider的get()函数将返回新的<T>的对象实例。

public 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的声明代码:

这样写完之后,我们就新拥有了一个注解@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
public class DripCoffeeModule {

   @Provides 

   Heater provideHeater(Executor executor) {
    return new CpuHeater(executor);
   }

分析:由于我们知道,provideHeater是在有别的类@Inject了Heater类(或子类)的时候,会来调用这个方法,也就是说这个完全是由Dagger框架来调用的,而并非传统意义上我们自己调用这个函数。

那么问题应该就很明显了吧???

这个参数,应该要由我们来提供吧?肯定有一个@Module里面有一个方法是叫做 provideExecutor(), 但是我们这边也不能用@Inject来注解这个参数。那要怎么让我们这个当前类,知道要去找那个方法拿这个参数呢?

有两种方法:

1.把这个provideExecutor()方法放入到我们这个类中来,这样子它就能够找到这个方法并进行注入这个参数对象了。

2.在@Module中加入一个参数 complete=false, 标记说明该Module为不完整的Module。因为不完整的Module允许缺少对象实例

@Module(complete=false)
public class DripCoffeeModule {

   @Provides 

   Heater provideHeater(Executor executor) {
     return new CpuHeater(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 , 重载其绑定关系。
下面这个JUnit测试利用Mockito, 重载了DripCoffeeModule的Heater绑定关系。这个Mock对象将inject到CoffeeMake中。

这种重载方式也很适合程序的小型变动, 例如付费版,免费版。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK