Android Dagger依赖注入框架浅析

今天接触了Dagger这套android的依赖注入框架(DI框架)。感受跟Spring 的IOC差点儿相同吧。这个框架它的优势是它没有採用反射技术(Spring是用反射的),而是用预编译技术。由于基于反射的DI很是地耗用资源(空间,时间)java

由于现在开发都是用Android Studio了,因此我这里大概讲下配置Dagger框架的开发环境。需要怎么作。android

(由于Android Studio中用Gradle。因此跟传统咱们用Eclipse配置的话。直接导入jar包,有点不同。git

github


在開始看个人博文前。但愿你们有时间可以本身看下Dagger官网的文档:http://square.github.io/dagger/app

        

相应的中文翻译:http://fanxu.me/post/2013-07-18#main(主要就是这篇文章翻译得很是具体,就是太长了,我主要对这文章进行了本身的理解还有本身的浅析哈,因此假设你时间充裕,并且足够耐心。可以先看这个。)框架


Dagger是构建在Android annotations的基础上的。假设你还不知道关于Android annotations,可以先看看我以前的一篇文章。ide


Android annotations浅析:http://blog.csdn.net/ljphhj/article/details/37601173函数


1、Android Studio 配置 Dagger 开源框架环境post

一、创建一个module字体

二、在module中会有build.gradle的文件

三、在文件里的下列位置增长两行红色字体

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.+'

}


2、Dagger浅析

一、不论什么新生事物的产生都有其特定的历史背景,为何会出现Dagger框架呢?

Dagger框架为了简化你的代码,让你把你的注意力转移到真正需要关注的类上,比方:

咱们常说要经过工厂类来建立出产品类对象。

但是,每每咱们更重视的是产品类,而并不是工厂类,更不是它的生产过程。


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

@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 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中来。

@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();
  }
}

这种重载方式也很是适合程序的小型变更, 好比付费版。免费版。

相关文章
相关标签/搜索