Dagger2入门(一)

1、Dagger2是什么?

首先,我们要明白Dagger2是Android中比较热门的依赖注入框架。什么是依赖注入呢?
通俗来讲,依赖注入就是一个类中需要依赖其他对象时,不需要你亲自为那些需要依赖的对象赋值,为那些对象赋值的操作都交给了IOC框架

Dagger2介绍

一般的IOC框架都是通过反射实现的。但是Dagger2作为android的框架,为了不影响其性能,它是通过apt动态生成的代码

Dagger2主要分为三个部分

1、依赖提供方Module,负责提供依赖中所需要的对象,实际编码中类似于工厂类
2、依赖需求方实例,他在实际编码中对应业务Activity.当你在Activity中需要某个对象的时候,你只需要声明就行了
3、依赖注入的组件Component,负责将对象注入在依赖需求方里面,它在实际编码中是一个接口,编译Dagger2会自动为它生成一个实现类

Dagger2的主要工作流程

1、将依赖需求房(Activity里面声明)传入给Component的实现类
2、Component实现类会根据依赖需求方实例中依赖声明,来确定实例需要依赖哪些对象
3、确定依赖对象后,Component会在与自己关联的Module类中查找有没有提供这些依赖对象的方法,有的话就将Module类中提供的对象设置到依赖需求方实例中

2、实例

在项目下的build.gradle文件中添加apt插件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.2.0'

// 添加android-apt 插件
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'

// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}

allprojects {
repositories {
jcenter()
}
}

task clean(type: Delete) {
delete rootProject.buildDir
}

在app目录的build.gradle文件中添加:

1
2
3
4
5
6
7
8
9
10
11
12
13
//应用apt插件
apply plugin: 'com.neenbedankt.android-apt'

...

dependencies {
...
//引入dagger2
compile 'com.google.dagger:dagger:2.4'
apt 'com.google.dagger:dagger-compiler:2.4'
//java注解
provided 'org.glassfish:javax.annotation:10.0-b28'
}

编写布料类Cloth

写一个Cloth类用作依赖对象,它包含一个color属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Cloth {
private String color;

public String getColor() {
return color;
}

public void setColor(String color) {
this.color = color;
}

@Override
public String toString() {
return color + "布料";
}
}

书写Module类

现在的需求是MainActivity中需要使用到Cloth对象,所以我们要为MainActivity书写一个Module类用来提供Cloth对象,相当于创建一个提供商

1
2
3
4
5
6
7
8
9
10
@Module
public class MainModule {

@Provides
public Cloth getCloth() {
Cloth cloth = new Cloth();
cloth.setColor("红色");
return cloth;
}
}
@Module:表面该类是Module类,Dagger2用来识别的
@Provides:声明Module类中,该方法是依赖对象的
在一个方法中声明@Provides注解,就相当于创建了一条生产线,这个生产线的产物就是该方法的返回值。有了这条生产线,
供应商就能提供这种类型的商品,当商店老板发现有人需要这种类型的商品时,供应商就可以提供给它了

书写Component接口

1
2
3
4
@Component(modules=MainModule.class)
public interface MainComponent {
void inject(MainActivity mainActivity);
}

和Module类一样,Component也需要注解声明的,那个注解就是@Component。
@Component注解的作用

@Component注解有两个属性,分别是modules和dependcies.这两个属性的类型都是Class数组
 1、Modules :声明Component含有几个Moudle。当Component需要某个依赖的时候,就会通过这些Module类中对应的方法获取依赖对象。
 MainComponent中只包含MainMoudle,所以令moudles=Maindule.class.这就相当于供应商和商店老板确定了合作关系

 2、dependencies :声明Component类的依赖关系,这个下面在详细讲解

接口中的那个方法的作用

我们现在之声明了Component类,但是我们要怎么将Component类和依赖需求方对象联合起来呢。这个就是那个方法的作用。
这个方法可以将需求方的依赖对象传送到Component类中。Component类就会根据依赖需求方对象中声明的依赖关系来注入
依赖需求方对象中所有的对象
注意:

本demo中,MainActivity中需要Cloth对象,所以我们通过inject方法将Activity的实例传入到依赖组件MainComponent中,依赖组件MainComponent就会从提供方MainMoudle中的getCloth方法中获取cloth实例,并将该实例赋值给依赖需求方MainActivity中的cloth字段,相当于你去商店的道路,没有这条路,你就无法去商店和老板说明你需要的东西。但是这里需要注意的是,inject方法的参数不能用父类来接收,例如本Demo中,如果inject的参数是Activity,那么Dagger2就会报错

在MainActivity中声明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class MainActivity extends AppCompatActivity {
private TextView tv;
@Inject
Cloth cloth;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv = (TextView) findViewById(R.id.tv);

MainComponent build = DaggerMainComponent.builder().mainModule(new MainModule()).build();
build.inject(this);
tv.setText("我现在有" + cloth);
}
}

上面代码有两处比较关键

1、声明依赖对象Cloth,就是在Cloth字段上添加@Inject注解,Dagger2中声明依赖对象都是通过@Inject注解的。
注意:@Inject注解字段不能是private和protected

2、通过dagger2自动生成的类来创建Component的实现类,创建时需要传入该ComPonent实现类所需要的Moudle类的实例。传入方法就是调用Moudle类类名首字母小写所对应的方法。这里我们通过Dagger2自动生成DaggerMainComponent类创建了。

MainComponent的实例,相当于我们创建了一个实实在在的商店,不在是理论上的商店,但是创建商店一定要创建真是的供应商,所以创建Component实现类时一定要传入Moudle实例.(注意编写完Component接口后Dagger2并不会自动创建对应的类,需要我们点击Android Studio中bulid菜单下的Rebulid Poject选项,或者直接书写代码,编译时Dagger2就会帮你自动生成).

再将MainActivity通过inject方法发送到MainComponent中,调用完inject方法后,你就会发现,MainActivity中的cloth字段已经被赋值,而且该cloth对应的就是我们在MainModule类的getCloth方法中创建的Cloth对象。

结果

另一种方法–构造函数声明@Inject,这样我们就不需要跟上面一样那么麻烦,又要写module类,又要写什么Component接口

创建依赖类 shope

1
2
3
4
5
6
7
8
9
10
public class Shoe {
@Inject
public Shoe() {
}

@Override
public String toString() {
return "鞋子";
}
}

这次我们创建的方式和Cloth不一样了,我们在构造方法上声明了@Inject注解。
这个注解的作用:当Component所拥有的Module类找不到依赖需求方需要类型的提供方法的时候,Dagger就会检查该需要类型有没有@Inject声明的构造方法,有的话就用该构造方法创建一个
(相当于你去商店购买东西,你需要的东西商店的供应商不生产,商店老板只好去网上看看有没有你需要的东西,有的话就帮你网购一个)

在MainActivity中声明Shope

1
2
3
4
5
6
7
8
9
10
11
public class MainActivity extends AppCompatActivity {
...
@Inject
Shoe shoe;

@Override
protected void onCreate(Bundle savedInstanceState) {
...
tv.setText("我现在有" + cloth + "和" + shoe);
}
}

结果

注意

为什么不都用这种方法来声明呢,为什么要用Moudle类?
这是因为项目中,我们会用到别人的jar包,我们无法修改别人的源码,就更别说在别人的代码上添加注解了,所以我们只能通过Module类来提供

Shope是在源码的构造方法上面添加@Inject
而前面的代码是通过Module类引进进来的。

例如,我们创建的这些依赖类都不用依赖其他依赖类,但是如果需要依赖其他的依赖类又要怎么弄呢?

创建依赖类Clothes

我们又要创建一个衣服类Clothes,制作衣服时需要布料,所以我们在创建Clothes的实例时需要用到Cloth实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Clothes{

private Cloth cloth;

public Clothes(Cloth cloth){
this.cloth=cloth;
}

public Cloth getCloth(){
return cloth;
}

public String toString(){
return cloth.getColor()+"衣服"
}
}

在Module类中增加提供方法

现在我们的MainActivity中需要依赖与Clothes对象,我们在MainActivity中添加提供Clothes对象的方法。但是Clothes需要依赖Cloth对象,这要怎么办呢?可能最先想到的办法是这样;

1
2
3
4
5
6
@Provides
public Clothes getClothes(){
Cloth cloth = new Cloth();
cloth.setColor("红色");
return new Clothes(cloth);
}

直接在方法中创建了一个Cloth方法不就得了,但是你有没有发现,创建Cloth的方法已经在getCloth()方法中有了,我们能不能用getCloth方法中创建的Cloth实例来创建Cloth实例呢?
Dagger2提供了这样的一个功能,我们只要在getClothes方法中添加Cloth参数,Dagger2就会像帮依赖需求方找依赖对象一样帮你找到该方法依赖的Cloth实例,所以我们的代码可以这样写:

1
2
3
4
@Provides
public Clothes getClothes(Cloth cloth){
return new Clothes(cloth);
}

在MainActivity中声明Clothes依赖

我们修改之前的MainActivity,添加一点东西

1
2
3
4
5
6
7
8
9
10
11
public class MainActivity extends AppCompatActivity {
...
@Inject
Clothes clothes;

@Override
protected void onCreate(Bundle savedInstanceState) {
...
tv.setText("我现在有" + cloth + "和" + shoe + "和" + clothes);
}
}

结果

依赖总结

同理,在带有@Inject注解的构造函数要是依赖其他对象,Dagger2也会帮你自动注入

我们有两种方式可以提供依赖,一个是注解的@Inject的构造方法,一个是在Module里提供的依赖,那么Dagger2是怎么进行选择的呢?

  • 步骤1、查找Module中是否存在创建该类的方法

  • 步骤2、若存在创建该类的方法,查看该方法是否存在参数

  • 步骤2.1、若存在参数,则按从步骤1开始依次初始化每个参数

  • 步骤2.2、若不存在参数,则直接初始化该类的实例,一次依赖注入到此结束

  • 步骤3、若不存在创建类的方法,则直接查找Inject注解的构造函数,看构造函数是否存在参数

  • 步骤3.1、若存在参数,则从步骤1开始依次初始化每个参数

  • 步骤3.2、若不存在参数,则直接初始化该类实例,一次依赖注入到此结束

也就是说,Dagger2会递归的提供依赖

坚持原创技术分享,您的支持将鼓励我继续创作!