Dagger2从入门到精通

一、环境搭建

在当前比较流行的AS环境下开发,配置比较简单。如果gradle plugin版本在2.2及以上。只需要在项目目录的build.gradle(注意不是根目录的build.gradle)的dependencies中添加

compile 'com.google.dagger:dagger:2.10'

annotationProcessor 'com.google.dagger:dagger-compiler:2.10'即可。具体如下图所示:

Dagger2从入门到精通_第1张图片
图1.1

当然这里有可能会报如下错误,向我就报了如下错误。

图1.2

解决办法也很简单,因为annotationProcessor是google在gradle2.2及以上引进的所以只需在根目录的build.gradle中添加对应的仓库即可。如下图所示:

Dagger2从入门到精通_第2张图片
图1.3

二、Dagger2简介

2.1 什么是Dagger2
    dagger2我想大家应该已经很熟悉了,它主要是用来是解决Android或java中依赖注入的一个类库(DI(Dependencies Inject)类库)。很明显的一个很实用的使用场景时,就是我们实例化一个对象时,都要使用一个关键字new,我不知道大家写了几年代码后,每次写这种体力活代码是什么感觉,反正我是受够了,估计谷歌工程师也受够了,所以开发这么一个DI类库吧,当然他Dagger2的作用永远不止这些,下面我会详细介绍。

2.2 Dagger2能干什么

一. 大大提高开发效率,减少体力活。

    上面我也举了例子就是实例化对象,使用Dagger2后,就再也不用写new了。还有就是实现单例时我们再也不需要考虑使用懒汉模式还是饿汉模式,是不是线程安全了,这些Dagger2都已经帮我们实现了。

. 对类实例进行更好的管理,并赋予其声明周期

  Dagger2引入了Component,Module的概念,具体下面我会详细介绍,暂时你把你可以理解,你需要给一个类成员属性赋值,那么肯定需要两个步骤,步骤一就是需要有一个地方实例化也就是平时调用new的地方,步骤二是赋值。那么在Dragger2中这两步分别由Module和Component的进行代替。而且通过
Component,Module,整个app的类实例结构变的很清晰。下面先看一个简单的图:

Dagger2从入门到精通_第3张图片
图2.1

三. 耦合度大大降低。
    之所以我们使用设计模式,其实主要还是减少程序之前的耦合度。举个很浅显的例子,比如我们构造函数变了,那么我们在需要使用的地方都需要改变,这不是一件很蛋疼的事。但是我们之前解决方案,尤其是使用三方库时,不会直接使用而是会包装一层,从而解决这种牵一发而动全身。然而Dagger2实现原理也是如此只是他帮我做了这件事而已。

三、Dagger2的使用

前面讲了这么多,现在让我们正式踏入Dagger2的使用。嗯嗯,在讲Dagger2之前我们先熟悉下Dagger2一些常用的注解。
1. @Component  该注解主要作用是充当需要注入目标对象(@Inject注解的属性)与产生实例对象(Module或者被@Inject注解的构造函数)桥梁作用,也就是我们生活中的媒婆。
2.@Inject 该注解主要标识需要注入的对象,以及实例对象的构造方法。
3.@Module 该注解作用和使用@Inject 标识到构造函数上一样,用来产生实例对象,为什么引入呢,主要是解决三方库,更准确说有些类我们无法在其构造函数添加@Inject注解。
4.@Provides 该注解主要提供实例化对象的方法,使用在@Module声明的类中。
5.@Qualifier 该注解主要用来解决Dagger2无法决定使用那个构造器来实例对象时的一种手段,因为Dragger2是根据返回值来判断当前方法是不是构建对象方法,那么如果两个函数返回值一样,那他该选择谁,这时候就需要使用该注解来解决这个问题。
6.@Named 该注解其实是 @Qualifier的一个默认实现。
7.@Scope  该注解主要标识创建的对象是否需要复用,就是没有Scope时,每次注入都创建新的对象而使用了Scope的注释以后,创建的对象会被复用。单例@Singleton就是其一个典型的使用场景。
8.@Singleton 该注解是 @Scope的一个默认实现
9.@Subcomponent 该注解主要用来建立Component之间的联系。

3.1 注解@Inject的使用

3.1.1 使用无参构造函数注入。具体步骤如下:      

Dagger2从入门到精通_第4张图片
图3.1
Dagger2从入门到精通_第5张图片
图3.2
Dagger2从入门到精通_第6张图片
图3.3

经过上面几步,在Activity声明的userBean就被实例化了。基本的Dagger2使用就到此结束了,是不是相当简单。这里我简单的说下Dagger2的实现原理,他主要使用编译时注解帮我们生成了一系列相关的类,这也是为什么Dagger2性能好的一个十分重要的原因。如下图所示:   

Dagger2从入门到精通_第7张图片
图3.4

然后使用工厂模式实例化相应对象,具体大家可以自己看看,生成的代码在build文件夹中。

3.1.2 使用有参构造函数注入,步骤如下。

Dagger2从入门到精通_第8张图片
图3.5
Dagger2从入门到精通_第9张图片
图3.6
Dagger2从入门到精通_第10张图片
图3.8
Dagger2从入门到精通_第11张图片
图3.9

代码比较简单,具体细节代码请看Demo例子,我就不详细介绍了。这里我主要说一下使用@Inject标识构造方法产生对象存在问题。第一。很多时候我们没法改代码加入@Inject注解,比如第三方库。第二。针对有参数的属性实例化时,Dagger2采用递归向上的方式依次实例化对象,那么最后一个对象的构造方法一定没有参数。也正是这个两个原因,Dagger2引入@Module 和 @Provides注解。下面就让我们一起揭开他们的面纱。

3.2 注解@Module @Provides的使用

3.2.1 使用无参构造函数注入。具体步骤如下:
       

Dagger2从入门到精通_第12张图片
图3.10
Dagger2从入门到精通_第13张图片
图3.11
Dagger2从入门到精通_第14张图片
图3.12

3.2.1 使用有参构造函数注入。具体步骤如下:
        有参构造函数注入我就不贴图,步骤和使用无参构造一模一样,只是在@Module注解的类中,在实例化对象时调用有参构造函数即可。不过我这里我需要说的是,不知道大家有没有疑惑如果我在同一个Module中,除了函数名称不一样的函数怎么办,如下图情景:

Dagger2从入门到精通_第15张图片
图3.13

我们知道Dagger2其实是根据返回值来判断是不是同一个实例化对象的方式,那如果返回值一样,那显然就有问题,因为他根本知道使用哪一个函数来构建对象。所以正式因为这个问题,才产生了@Qualifier注解。
      所以针对上面问题我们解决方式如下:现自定义个使用@Qualifier注解的注解。

Dagger2从入门到精通_第16张图片
图3.14

然后在@Module注解的类中分别使用刚刚定义的注解标识,代码如下:

Dagger2从入门到精通_第17张图片
图3.15

这里注意红色框框代码,使用的注解是@Named而不是@MY_NMAE,其实聪明的你就肯定就会想到,一个是Dagger2自带的,一个是我模仿自定义的。没错事实就是这样@Named就是@Qualifier的默认实现,他们完全是可以互换的。不知道你们是不是明白了呢

3.3 注解@Scope的使用
      前面我也说了,这个注解主要是通知Dagger2复用已经创建实例对象的。那么问题来了,我们平时一个典型的单例模式不就是复用对象吗?那我们如何使用该注解来做到这点呢。其实也很简单,因为Dagger2默认都是重新创建实例对象,那如果我们想复用就得使用@Scope注解,然而也就是感觉给实例对象和Component建立了一个生命周期绑定一样,所以要实现单例,在首先使用@Scope注解的同时,还必须保证相对应的Component对象唯一。具体怎么说,请看下面:
    第一:自定义一个由@Scope注解的注解,如下图所示:

Dagger2从入门到精通_第18张图片
图3.16

    第二:在@Module注解的类中使用刚刚自定义注解

Dagger2从入门到精通_第19张图片
图3.17

    第三:也是最关键的一步,就是在@Component注解的类上使用该注解,不使用会编译不过。

Dagger2从入门到精通_第20张图片
图3.18

    第四:在Activity使用,看多个对象地址是否相同(下图两个对象地址输出相同)

Dagger2从入门到精通_第21张图片
图3.19

经过上面步骤后,单例模式就实现了,这里要告知大家的@Singleton注解和我自定的注解作用一模一样,只是一个是Dagger2自带的一个是我自己自定义的。讲到这里不知道大家有没有意识一个问题就是基本一个APP可以分成几个Component,比如Application是一个全局对象,那么我们就应该对应一个ApplicationComponent预知对应,也就是说我们可以把一个App根据声明周期不同模块划分,分成不同的Component.每个页面当然也具有自己生命周期也可以当成一个Component例如ActivityComponent.那这里就有一个问题了Component之间如何建立关联呢,比如我们全局的Component构造的对象如何是其他Componet共享这个对象呢?带着这个疑问我们开始我们这篇文章最后一部分,也是Dagger2最后一个知识点.Component之间如何关联。

3.4 Component之间联系的建立
      在开始之前我们先说一个一个场景,就是原来我们总是在Appication初始化一些工具类或者三方库,而这些类基本是单例的,在各个地方也是共享的,比如Activity中的使用。那如果使用Dagger2怎么做呢?其实很简单就是使用我们上面介绍的@Scope注解的具体实现@Singleton再结合本节内容就很容易实现了。
3.4.1 component之间关系建立方式
      1. 使用dependencies,具体代码如下:

Dagger2从入门到精通_第22张图片
图3.20

    2.使用@Subcomponent注解,具体代码如下

Dagger2从入门到精通_第23张图片
图3.21

3.4.2 如何实现全局单例模式以及相应实例对象在其他Component中共享
      1.首先我们需要针对Application建立一个ApplicationComponent,因为前面讲解@Scope也说了,实现一个单例模式,在使用@Scope注解前提下,还有一个很重要的条件就是相应Component对象唯一,因为Application对象是唯一的,所以我们建立一个ApplicationComponet作为全局单例模式是可以的。具体步骤如下图 :

Dagger2从入门到精通_第24张图片
图3.22

这里需要注意红色框框区域,因为我们全局创建的对象ApplicationBean需要被其他Component共享,所以必须书写红色区域代码,方法名是随意的。
      2.我们需要使用dependencies或者@Subcomponent注解方式与上面ApplicationDependencyComponent建立关联,具体实现分别如下图:

Dagger2从入门到精通_第25张图片
图3.23
Dagger2从入门到精通_第26张图片
图3.24

这里有一个十分需要注意的是就是上面红色框框标识的注解,该注解一定要加,不然编译不过,这是因为我们依赖的父ApplicationDependencyComponent有一个@Singleton对应的Scope,Dagger2要求与之关联的Component必须也有Scope并且不能和父的相同。具体@ForActivity注解如下图:

Dagger2从入门到精通_第27张图片
图3.25

四 总结
      关于dagger2的所有的知识点到此终于结束了 。这是我的第一篇在上写文章,之前一直在csdn。以后也会持续下去,希望能帮助大家,谢谢。

本文代码地址

你可能感兴趣的:(Dagger2从入门到精通)