基本概念
Dagger2 依赖注入框架,用来解耦类关系。特别是 MVP 分层结构中,方便解耦和分层测试A fast dependency injector for Android and Java.
仓库地址:https://github.com/google/dagger  
依赖
- build.gradle
 
1  | dependencies {  | 
- app/build.gradle
 
1  | apply plugin: 'com.neenbedankt.android-apt'  | 
注解关键字
Inject
- 标注变量
要求Dagger为该变量提供依赖,实例化该变量。不能用private修饰符修饰该变量 - 标注构造函数
@Inject标记了的变量在需要这个类实例的时候,Dagger会找到这个构造函数并将其实例化。也就是@Inject标记的构造函数为@Inject标记的变量提供依赖(实例化)。构造函数的参数不需要使用NonNull标注,Dagger会做强制检测 - 标注方法
标注的方法会自动在构造函数完成实例化后调用 
Module
格式:@Module(includes = {*module.class})@Module 注释类,用于标注提供依赖的类。和被 @Inject 标记的构造函数功能类似。但是有时候提供依赖的构造函数是第三方库,无法通过 @Inject 来标注;而且如果构造函数带参数,@Inject 也无法提供参数,所以需要 @Module 来标注。Module 类是一个简单工厂模式,通过 @Provider 提供三个重要功能:   
- 给 
@Inject变量提供实例 - 给 
@Inject构造函数提供参数 - 给其他 
@Module中的@Provider方法提供参数 
@Inject(构造函数)和 @Module 都可以提供依赖(实例化), Dagger2 会如何选择呢?具体规则如下:  
1  | 步骤1:首先查找 @Module 标注的类中是否存在提供依赖的方法 @Provider。  | 
所以:创建类实例级别 @Module 维度要高于 @Inject 维度   
Provider
注释方法,以 provide 作为前缀,必须出现在有 @Module 注释的类中   
它是根据返回值类型来标识和匹配的,方法名并不重要,只需要保证以
provide开头方便阅读即可
这里细化下 @Module 通过 @Provider 提供的三个功能:  
- 给 
@Inject变量提供实例
也就是将实例化放到了Module中 
1  | 
  | 
- 给 
@Inject构造函数提供参数 
1  | // 构造函数及所需参数  | 
对应类构造函数所需参数,可以通过 Module 的构造函数,将参数传递进来  
1  | // 对应 module 代码  | 
- 给其他 
@Module中的@Provider方法提供参数
该方式需要通过后面介绍的@Component将多个@Module类连接起来才能生效。给其他@Module中的@Provider方法提供参数依赖(实例化) 
1  | @Module  | 
Binds
用来简化 @Provider 的注解     
- 被注解的方法必须是抽象
 - 必须指定返回类型,也就是类似 
@Provider的返回值 
示例:将 Random 绑定到 SecureRandom 上:  
1  | 
  | 
Component
接口或抽象类,是依赖需求方 (@Inject) 和依赖提供方 (@Module) 之间的桥梁。在编译过程中 Dagger 会自动生成对应的实现类 Dagger***Component   
如果
@Component标记的类不是顶级类,生成的实现类格式为DaggerA_B_function,类结构之间增加下划线
注解格式
格式:@Component(dependencies = {*component.class}, modules = {*modules.class})
参数 dependencies :表示 Component 和 Component 之间的依赖关系
参数 module :表示依赖提供方
示例:  
1  | 
  | 
@Component 注解的接口或抽象类
必须包含 Provision methods 或者 Members-injection methods 其中的一种方法  
1  | @Singleton  | 
Provision methods:该方法没有参数,但是返回值必须为 @Inject 注解变量的类型或者注解过的构造函数的类(也就是依赖需求方),或者为 @Provider 返回值类型:DataRepository getDataRepository();  
Members-injection methods:该方法满足下面条件之一:  
- 返回值为 
void或者 传入的参数类型,此时方法必须且只能包含一个参数,该参数为@Inject注解所在类的类型 
1  | // @Inject 在 TaskDetailActivity 中  | 
- 返回值为 
MembersInjector类型,可以没有参数MembersInjector<SomeType> getSomeTypeMembersInjector(); 
获取 Component 实例
格式:Dagger***Component.builder().***初始化参数***.build(),举例:   
1  | mTasksRepositoryComponent = DaggerTasksRepositoryComponent.builder()  | 
@Component 在实例化时,会将对应的所有 @Module 全都实例化,此时可以通过 @Module 构造函数传入参数     
@Component.Builder
默认是不需要重新定义的。如果需要通过 @Component 将参数传递给 @Module 的 @Provider 中,我们可以在这里定义 @BindsInstance  
1  | @Component.Builder  | 
需要注意 @Component.Builder 需要满足几点条件,具体参考 Dagger 的文档。其中必须有一个 build 返回值为 @Component 类, 每次只能传递一个参数并且返回值为 Builder 类型。 
Scope
用于自定义的注解,限定注解作用域,实现局部的单例
Singleton
注解表示为单例模式。但是 Dagger 并没有真正的创建单例,需要我们手动写代码确保只实例化一次。@Singleton 需要同时标记 Provider 和 Component,并且我们把实例化该 Component 放到 Application.onCreate 中,确保只会实例化一次,这样才能体现出单例模式的效果。作用:  
- 更好的管理 
Component和Module之间的关系,保证它们是匹配的。如果两者的Scope是不一样的,则在编译时报错 - 代码可读性,更好的了解 
Module中创建的类实例是单例 
Qulifier
用于自定义注解,精确指定 @Module 和 Inject 的依赖对应关系。@Module 来标注提供依赖的方法时,方法名可以随便定义的,但是为了增加可读性,一般以 provide 开头。Dagger2 主要是根据返回值的类型来决定为哪个被 @Inject 标记了的变量赋值。但是如果有多个一样的返回类型,就无法区分了。因此使用 @Qulifier 来自定义一个注解,然后通过自定义的注解去标注提供依赖的方法和依赖需求方,这样就能精确匹配了。
一个更为精简的定义:当类型不足以鉴别一个依赖的时候,我们就可以使用@Qulifier 这个注解标示。  
常规步骤
引入依赖文件
在 build.gradle 和 app/build.gradle 中添加依赖,并选择 Sync Project 同步并下载这些第三方包    
Inject 标注
标注需要依赖的变量,或者提供依赖的构造函数
实现 @Module 和 @Provider
根据 Inject 来设计 Module   
Inject标注了构造函数Module可以提供构造函数所需参数Inject没有标注构造函数Module直接实例化并提供返回值
实现 @Component
- 指定范围 
Scoped - 选定依赖和指定对应的 
Module @Inject标注的依赖变量,如果需要实例化,需要将变量所在类传递给Componentvoid inject(TaskDetailActivity activity);
编译项目
Dagger 自动生成 Module 和 Component 对应的文件    
关联注入
Dagger***Component 实现注入并实例化 @Inject 标注的变量    
注意
如果觉得 Dagger 生成的不对,可以 clean 整个工程,重新生成一次。有的时候只修改 @Inject 注解,并不会正确的生成对应代码,需要 clean & remake 才行   
常见错误
没有提供依赖实例化方法
@Inject 注解成员后,没有注解构造函数,导致如下错误:  
1  | C:\Users\xmt\AndroidStudioProjects\xmt\gitlab\02_myTodo\myTodo\app\src\main\java\com\ymzs\todo\mytodo\ToDoApplication.java  | 
@Component 没有定义范围
没有定义 scoped 的 @Component 不能依赖一个定义了 scoped 的 @Component,否则导致如下报错:  
1  | Error:(13, 1) 错误: com.***.mytodo.tasks.TasksPresenterComponent (unscoped) cannot depend on scoped components:  | 
@Inject 标注私有字段
@Inject 不支持注解私有字段,否则会导致如下错误:  
1  | Error:(5, 33) 错误: 找不到符号  | 
@Binds 注解的方法,没有提供输入参数
Error:(18, 20) 错误: android.app.Application cannot be provided without an @Inject constructor or from an @Provides-annotated method.
被 @Binds 注解的方法,必须要提供一个 @Inject 构造函数或者一个 @Provides 注解的方法:  
1  | @Binds  | 
@Component.Builder 必须要有一个无参方法返回 @Component 类型
Error:(21, 5) 错误: @Component.Builder types must have exactly one no-args method that  returns the @Component type
必须要有一个无参方法返回 @Component 类型,这个可以理解为 build() 提供该 Component 的实例   
1  | @Component.Builder  |