Android Dagger依赖注入框架浅析

转至:  http://blog.csdn.net/ljphhj/article/details/37663071


今天接触了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.+'

}

二、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; 

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中明确说明。

@Module(
    staticInjections = LegacyCoffeeUtils.class
)
class LegacyModule {
}

可以使用ObjectGraph.injectStatics()注入静态变量:

ObjectGraph objectGraph = ObjectGraph.create(new LegacyModule());
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 provide Others (){ 

return new Others; 

}

分析:由于ClassRoom中只用到了一个Student的类,而injects列表中也只写了ClassRoom.class, 这样的话,这个类提供的其他方法有可能被除了ClassRoom之外的类所用,那么避免报错就要在@Module加上 参数library=true

9.所有@Provides要放在一个@Module中

由于Dagger规定 所有@Provides要放在一个@Module中,所以我们要么可以在一个Module中用includes参数把其他的Module类包含进来

或者比较建议的是:再创建一个空的Module类,把所有的Module都包含到这个Module中来。

@Module(
  includes = {
    StudentModule.class,
    ExecutorModule.class
  }
)
public class AppModules {
}

10.Module重载(引用官网的例子): 了解下就可以了

若对同一个依赖关系有多个@Provides函数, Dagger 将会报错。但在有些情况下,是有必要替换production代码的, 比如测试和开发。 在@Module中可以使用overrides =true , 重载其绑定关系。

下面这个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();
  }
}

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


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值