7.Dagger2 入门

一、首先你要知道什么是依赖?

想要理解Dagger2,首先你要理解一个概念,就是什么是依赖,懂的同学可以省过此段。这里给大家举个通俗易懂的例子,让你秒懂什么是依赖,你今天去办港澳通行证,出入境告诉你想办理这个证你要携带你的个人身份证和户口本,不然不给你办,这个时候就产生了一个依赖关系,通行证的办理需要依赖身份证和户口本,我们就想到用代码实现可以如下。

 class PassPort{  

private IDCardBean mIDCard;  
private FamilyRegisterBean mFamilyRegister;  

public PassPort(){  
this.mIDCard = new IDCardBean();  
this.mFamilyRegister = new FamilyRegisterBean();  
}  
 }  

或者

class PassPort{  
private IDCardBean mIDCard;  
private FamilyRegisterBean mFamilyRegister;  

public PassPort(IDCardBean mIDCard, FamilyRegisterBean mFamilyRegister){  
this.mIDCard = mIDCard;  
this.mFamilyRegister = mFamilyRegister;  
 }  
}  

IDCardBean mCard = new IDCardBean();  
FamilyRegisterBean mFR = new FamilyRegisterBean();  

 PassPort mPassPort = new PassPort(mCard, mFR);  
}  

上面两段代码咋一看没啥毛病,但是如果现在我告诉你,你去办理身份证也同时需要带户口本还有身份证照片回执等,办理户口本还需要各种证明等,那么这个时候 IDCardBean 和 FamilyRegisterBean 的构造函数是不是也要相应地做出改变,而通行证 PassPort 的代码是不是也要做出修改,这时就有人说了,没事啊我可以改,那么如果你项目中有一百个地方都用到了呢,你是不是都要逐个逐个去改呢?为了不需要改动太多地方,我们必须给通行证和身份证、户口本进行降耦。

二、知道依赖的概念后,你要知道Dagger2是个什么东西

从上面的例子可以看出,如果按照我们日常的编码习惯,如果存在依赖关系的某一处有一些小修改,都可能会给代码量大的项目造成很大的改动,这样是不符合工程思想的,所以就出现了各种依赖注入框架,Dagger2就是其中的一个,它的前身是Dagger1,Dagger1是由Square公司开发的,而Dagger2却是由谷歌公司接手开发,两者还是存在一些区别的,不过这篇文章不会做太多介绍,主要还是教会大家如何使用Dagger2。

三、初步了解Dagger2后,你需要知道几个重要的概念

在正式使用Dagger2之前,你需要了解以下几个重要的注解,为了方便大家的理解,我会用最通俗的话来解释,如果想知道更多的可以查看官方文档。

1、@Inject:目标类用这个注解修饰的一些方法或者属性(字段),说明这个目标类需要这个依赖,比如上面例子中就可以在身份证字段和户口本字段上加上这个注解

2、@Module:用这个注解修饰的类说明是一个提供依赖的类,比如我们可以把通行证的依赖身份证和户口本一起放在用这个注解修饰的类中

3、@Provide:这个注解主要用来修饰 @Module 修饰的类中的方法

4、@Component:这个是把 @Inject 和 @Module 关联在一起的桥梁

5、@Scope:作用域

四、你需要知道Dagger2的使用步骤

1、定义所需要的bean,比如前面的 PassPort 、IDCardBean 之类的,然后在需要注入依赖的地方标上 @Inject 注解。

2、定义一个类,命名为 XXXModule ,比如 PassPortModule,用 @Module 注解这个类。

3、在PassPortModule 这个类中定义你需要提供依赖的方法,这些方法名一般为 provideXXX,而且要用 @Provides 来注解,方法体就是 new 一个依赖实例,比如 new 一个 IDCardBean 实例。

4、创建一个接口,一般命名为 XXXComponent,接口要用 @Component 注解,并需要传入相应的 module 参数,例如 @Component(modules = PassPortModule.class),另外需要提供一个注入方法,一般写成 void inject(目标类 tmp),比如 void inject(PassPort passPort)

五、开始上代码

1、在根项目(project)的 build.gradle 中添加以下代码,为了添加 apt 插件

dependencies {
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
}

2、在 app (module)的 build.gradle 中添加以下代码

dependencies {
apt 'com.google.dagger:dagger-compiler:2.6'
compile 'com.google.dagger:dagger:2.6'
}

注意上面两个步骤的版本可能会有所不一样。

3、定义PassPortActivity,并在需要依赖的属性上用 @Inject 注解

 public class PassPortActivity extends BaseActivity{  

@Inject  
IDCardBean mIDCard;  

@Inject  
FamilyRegisterBean mFamilyRegister;  

@Override  
protected void onCreate(Bundle savedInstanceState) {  
super.onCreate(savedInstanceState);  
setContentView(getContentViewId());  
}  
}  

4、定义PassPortModule,并用 @Module 注解这个类,类里面写上提供依赖的方法,方法用@Provides注解,方法体则是 new 出依赖实例

@Module  
public class PassPortModule {  
private final PassPortActivity mView;  

public PassPortModule(PassPortActivity view) {  
this.mView = view;  
}  

@PerActivity  
@Provides  
 public IDCardBean provideIDCard() {  
return new IDCardBean(mView);  
 }  

 @PerActivity  
@Provides  
 public FamilyRegisterBean provideFamilyRegister() {  
return new FamilyRegisterBean(mView);  
}  
}  

5、定义PassPortComponent

 @PerActivity  
 @Component(dependencies = ApplicationComponent.class, modules = PassPortModule.class)  
 public interface PassPortComponent {   
  void inject(PassPortActivity activity);  
 }  

6、这个时候依赖关系就已经建立好了,你需要重新编译一下你的工程,Android studio 的快捷键是 ctrl+f9

7、在你 onCreate 方法里面添加以下代码

  DaggerPassPortComponent.builder()  
 .applicationComponent(getAppComponent())  
 .passPortModule(new PassPortModule(this))  
.build()  
.inject(this);
来自:https://blog.csdn.net/interface_l/article/details/69946685#

Dagger-匕首,鼎鼎大名的Square公司旗下又一把利刃(没错!还有一把黄油刀,唤作ButterKnife);故此给本篇取名神兵利器Dagger2。

Dagger2起源于Dagger,是一款基于Java注解来实现的完全在编译阶段完成依赖注入的开源库,主要用于模块间解耦、提高代码的健壮性和可维护性。Dagger2在编译阶段通过apt利用Java注解自动生成Java代码,然后结合手写的代码来自动帮我们完成依赖注入的工作。

起初Square公司受到Guice的启发而开发了Dagger,但是Dagger这种半静态半运行时的框架还是有些性能问题(虽说依赖注入是完全静态的,但是其有向无环图(Directed Acyclic Graph)还是基于反射来生成的,这无论在大型的服务端应用还是在Android应用上都不是最优方案)。因此Google工程师Fork了Dagger项目,对它进行了改造。于是变演变出了今天我们要讨论的Dagger2,所以说Dagger2其实就是高配版的Dagger。

依赖注入(Dependency Injection)

那么什么是依赖注入呢?在解释这个概念前我们先看一小段代码:

public class Car{

    private Engine engine;

    public Car(){
        engine = new Engine();
    }
}

这段Java代码中Car类持有了对Engine实例的引用,我们称之为Car类对Engine类有一个依赖。而依赖注入则是指通过注入的方式实现类与类之间的依赖,下面是常见的三种依赖注入的方式:

1、构造注入:通过构造函数传参给依赖的成员变量赋值,从而实现注入。

public class Car{

    private Engine engine;

    public Car(Engine engine){
        this.engine = engine;
    }
}

2、接口注入:实现接口方法,同样以传参的方式实现注入。

public interface Injection<T>{

    void inject(T t);
}

public class Car implements Injection<Engine>{

    private Engine engine;

    public Car(){}

    public void inject(Engine engine){
        this.engine = engine;
    }

}

3、注解注入:使用Java注解在编译阶段生成代码实现注入或者是在运行阶段通过反射实现注入。

public class Car{

    @Inject
    Engine engine;

    public Car(){}
}

前两种注入方式需要我们编写大量的模板代码,而机智的Dagger2则是通过Java注解在编译期来实现依赖注入的。

为什么需要依赖注入

我们之所是要依赖注入,最重要的就是为了解耦,达到高内聚低耦合的目的,保证代码的健壮性、灵活性和可维护性。

下面我们看看同一个业务的两种实现方案:

1、方案A

public class Car{

    private Engine engine;
    private List<Wheel> wheels;

    public Car(){
        engine = new Engine();
        wheels = new ArrayList<>();
        for(int i = 0; i < 4; i++){
            wheels.add(new Wheel());
        }
    }

    public void start{
        System.out.println("启动汽车");
    }
}

public class CarTest{

    public static void main(String[] args){
        Car car = new Car();
        car.start();
    }
} 

2、方案B

public class Car{

    private Engine engine;
    private List<Wheel> wheels;

    public Car(Engine engine, List<Wheel> wheels){
        this.engine = engine;
        this.wheels = wheels;
    }

    public void start{
        System.out.println("启动汽车");
    }
}

public class CarTest{

    public static void main(String[] args){

        Engine engine = new Engine();
        List<Wheel> wheels = new ArrayList<>();
        for(int i = 0; i < 4; i++){
            wheels.add(new Wheel());
        }
        Car car = new Car(engine, wheels);
        car.start();
    }
}

方案A:由于没有依赖注入,因此需要我们自己是在Car的构造函数中创建Engine和Wheel对象。

方案B:我们手动以构造函数的方式注入依赖,将engine和wheels作为参数传入而不是在Car的构造函数中去显示的创建。

方案A明显丧失了灵活性,一切依赖都是在Car类的内部创建,Car与Engine和Wheel严重耦合。一旦Engine或者Wheel的创建方式发生了改变,我们就必须要去修改Car类的构造函数(比如说现在创建Wheel实例的构造函数改变了,需要传入Rubber(橡胶)了);另外我们也没办法替换动态的替换依赖实例(比如我们想把Car的Wheel(轮胎)从邓禄普(轮胎品牌)换成米其林(轮胎品牌)的)。这类问题在大型的商业项目中则更加严重,往往A依赖B、B依赖C、C依赖D、D依赖E;一旦稍有改动便牵一发而动全身,想想都可怕!而依赖注入则很好的帮我们解决了这一问题。

为什么是Dagger2

无论是构造函数注入还是接口注入,都避免不了要编写大量的模板代码。机智的猿猿们当然不开心做这些重复性的工作,于是各种依赖注入框架应用而生。但是这么多的依赖注入框架为什么我们却偏爱Dagger2呢?我们先从Spring中的控制反转(IOC)说起。

谈起依赖注入,做过J2EE开发的同学一定会想起Spring IOC,那通过迷之XML来配置依赖的方式真的很让人讨厌;而且XML与Java代码分离也导致代码链难以追踪。之后更加先进的Guice(Android端也有个RoboGuice)出现了,我们不再需要通过XML来配置依赖,但其运行时实现注入的方式让我们在追踪和定位错误的时候却又万分痛苦。开篇提到过Dagger就是受Guice的启发而开发出来的;Dagger继承了前辈的思想,在性能又碾压了它的前辈Guice,可谓是长江后浪推前浪,前浪死在沙滩上。

又如开篇我在简介中说到的,Dagger是一种半静态半运行时的DI框架,虽说依赖注入是完全静态的,但是生成有向无环图(DAG)还是基于反射来实现,这无论在大型的服务端应用还是在Android应用上都不是最优方案。升级版的Dagger2解决了这一问题,从半静态变为完全静态,从Map式的API变成申明式API(@Module),生成的代码更优雅高效;而且一旦出错我们在编译期间就能发现。所以Dagger2对开发者的更加友好了,当然Dagger2也因此丧失了一些灵活性,但总体来说利还是远远大于弊的。

前面提到这种A B C D E连续依赖的问题,一旦E的创建方式发生了改变就会引发连锁反应,可能会导致A B C D都需要做针对性的修改;但是骚年,你以为为这仅仅是工作量的问题吗?更可怕的是我们创建A时需要按顺序先创建E D C B四个对象,而且必须保证顺序上是正确的。Dagger2就很好的解决了这一问题(不只是Dagger2,在其他DI框架中开发者同样不需要关注这些问题)。

Dagger2注解

开篇我们就提到Dagger2是基于Java注解来实现依赖注入的,那么在正式使用之前我们需要先了解下Dagger2中的注解。Dagger2使用过程中我们通常接触到的注解主要包括:@Inject, @Module, @Provides, @Component, @Qulifier, @Scope, @Singleten。

  • @Inject:@Inject有两个作用,一是用来标记需要依赖的变量,以此告诉Dagger2为它提供依赖;二是用来标记构造函数,Dagger2通过@Inject注解可以在需要这个类实例的时候来找到这个构造函数并把相关实例构造出来,以此来为被@Inject标记了的变量提供依赖;

  • @Module:@Module用于标注提供依赖的类。你可能会有点困惑,上面不是提到用@Inject标记构造函数就可以提供依赖了么,为什么还需要@Module?很多时候我们需要提供依赖的构造函数是第三方库的,我们没法给它加上@Inject注解,又比如说提供以来的构造函数是带参数的,如果我们之所简单的使用@Inject标记它,那么他的参数又怎么来呢?@Module正是帮我们解决这些问题的。

  • @Provides:@Provides用于标注Module所标注的类中的方法,该方法在需要提供依赖时被调用,从而把预先提供好的对象当做依赖给标注了@Inject的变量赋值;

  • @Component:@Component用于标注接口,是依赖需求方和依赖提供方之间的桥梁。被Component标注的接口在编译时会生成该接口的实现类(如果@Component标注的接口为CarComponent,则编译期生成的实现类为DaggerCarComponent),我们通过调用这个实现类的方法完成注入;

  • @Qulifier:@Qulifier用于自定义注解,也就是说@Qulifier就如同Java提供的几种基本元注解一样用来标记注解类。我们在使用@Module来标注提供依赖的方法时,方法名我们是可以随便定义的(虽然我们定义方法名一般以provide开头,但这并不是强制的,只是为了增加可读性而已)。那么Dagger2怎么知道这个方法是为谁提供依赖呢?答案就是返回值的类型,Dagger2根据返回值的类型来决定为哪个被@Inject标记了的变量赋值。但是问题来了,一旦有多个一样的返回类型Dagger2就懵逼了。@Qulifier的存在正式为了解决这个问题,我们使用@Qulifier来定义自己的注解,然后通过自定义的注解去标注提供依赖的方法和依赖需求方(也就是被@Inject标注的变量),这样Dagger2就知道为谁提供依赖了。----一个更为精简的定义:当类型不足以鉴别一个依赖的时候,我们就可以使用这个注解标示;

  • @Scope:@Scope同样用于自定义注解,我能可以通过@Scope自定义的注解来限定注解作用域,实现局部的单例;

  • @Singleton:@Singleton其实就是一个通过@Scope定义的注解,我们一般通过它来实现全局单例。但实际上它并不能提前全局单例,是否能提供全局单例还要取决于对应的Component是否为一个全局对象。

我们提到@Inject和@Module都可以提供依赖,那如果我们即在构造函数上通过标记@Inject提供依赖,有通过@Module提供依赖Dagger2会如何选择呢?具体规则如下:

  • 步骤1:首先查找@Module标注的类中是否存在提供依赖的方法。

  • 步骤2:若存在提供依赖的方法,查看该方法是否存在参数。

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

    • b:若不存在,则直接初始化该类实例,完成一次依赖注入。

  • 步骤3:若不存在提供依赖的方法,则查找@Inject标注的构造函数,看构造函数是否存在参数。

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

    • b:若不存在,则直接初始化该类实例,完成一次依赖注入。

Dagger2使用入门

前面长篇大论的基本都在介绍概念,下面我们看看Dagger2的基本应用。关于Dagger2的依赖配置就不在这里占用篇幅去描述了,大家可以到它的github主页下去查看官方教程https://github.com/google/dagger。接下来我们还是拿前面的Car和Engine来举例。

1、案例A

Car类是需求依赖方,依赖了Engine类;因此我们需要在类变量Engine上添加@Inject来告诉Dagger2来为自己提供依赖。

public class Car {

    @Inject
    Engine engine;

    public Car() {
        DaggerCarComponent.builder().build().inject(this);
    }

    public Engine getEngine() {
        return this.engine;
    }
}

Engine类是依赖提供方,因此我们需要在它的构造函数上添加@Inject

public class Engine {

    @Inject
    Engine(){}

    public void run(){
        System.out.println("引擎转起来了~~~");
    }
}

接下来我们需要创建一个用@Component标注的接口CarComponent,这个CarComponent其实就是一个注入器,这里用来将Engine注入到Car中。

@Component
public interface CarComponent {
    void inject(Car car);
}

完成这些之后我们需要Build下项目,让Dagger2帮我们生成相关的Java类。接着我们就可以在Car的构造函数中调用Dagger2生成的DaggerCarComponent来实现注入(这其实在前面Car类的代码中已经有了体现)

public Car() {
    DaggerCarComponent.builder().build().inject(this);
}

2、案例B

如果创建Engine的构造函数是带参数的呢?比如说制造一台引擎是需要齿轮(Gear)的。或者Eggine类是我们无法修改的呢?这时候就需要@Module和@Provide上场了。

同样我们需要在Car类的成员变量Engine上加上@Inject表示自己需要Dagger2为自己提供依赖;Engine类的构造函数上的@Inject也需要去掉,应为现在不需要通过构造函数上的@Inject来提供依赖了。

public class Car {

    @Inject
    Engine engine;

    public Car() {
        DaggerCarComponent.builder().markCarModule(new MarkCarModule())
                .build().inject(this);
    }

    public Engine getEngine() {
        return this.engine;
    }
}

接着我们需要一个Module类来生成依赖对象。前面介绍的@Module就是用来标准这个类的,而@Provide则是用来标注具体提供依赖对象的方法(这里有个不成文的规定,被@Provide标注的方法命名我们一般以provide开头,这并不是强制的但有益于提升代码的可读性)。

@Module
public class MarkCarModule {

    public MarkCarModule(){ }

    @Provides Engine provideEngine(){
        return new Engine("gear");
    }
}

接下来我们还需要对CarComponent进行一点点修改,之前的@Component注解是不带参数的,现在我们需要加上modules = {MarkCarModule.class},用来告诉Dagger2提供依赖的是MarkCarModule这个类。

@Component(modules = {MarkCarModule.class})
public interface CarComponent {
    void inject(Car car);
}

Car类的构造函数我们也需要修改,相比之前多了个markCarModule(new MarkCarModule())方法,这就相当于告诉了注入器DaggerCarComponentMarkCarModule提供的依赖注入到了Car类中。

public Car() {
   DaggerCarComponent.builder()
           .markCarModule(new MarkCarModule())
           .build().inject(this);
}

这样一个最最基本的依赖注入就完成了,接下来我们测试下我们的代码。

public static void main(String[] args){
    Car car = new Car();
    car.getEngine().run();
}

输出

引擎转起来了~~~

3、案例C

那么如果一台汽车有两个引擎(也就是说Car类中有两个Engine变量)怎么办呢?没关系,我们还有@Qulifier!首先我们需要使用Qulifier定义两个注解:

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface QualifierA { }
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface QualifierB { }

同时我们需要对依赖提供方做出修改

@Module
public class MarkCarModule {

    public MarkCarModule(){ }

    @QualifierA
    @Provides
    Engine provideEngineA(){
        return new Engine("gearA");
    }

    @QualifierB
    @Provides
    Engine provideEngineB(){
        return new Engine("gearB");
    }
}

接下来依赖需求方Car类同样需要修改

public class Car {

    @QualifierA @Inject Engine engineA;
    @QualifierB @Inject Engine engineB;

    public Car() {
        DaggerCarComponent.builder().markCarModule(new MarkCarModule())
                .build().inject(this);
    }

    public Engine getEngineA() {
        return this.engineA;
    }

    public Engine getEngineB() {
        return this.engineB;
    }
}

最后我们再对Engine类做些调整方便测试

public class Engine {

    private String gear;

    public Engine(String gear){
        this.gear = gear;
    }

    public void printGearName(){
        System.out.println("GearName:" + gear);
    }
}

测试代码

public static void main(String[] args) {
    Car car = new Car();
    car.getEngineA().printGearName();
    car.getEngineB().printGearName();
}

执行结果:

GearName:gearA
GearName:gearB

4、案例D

接下来我们看看@Scope是如何限定作用域,实现局部单例的。

首先我们需要通过@Scope定义一个CarScope注解:

@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface CarScope {
}

接着我们需要用这个@CarScope去标记依赖提供方MarkCarModule。

@Module
public class MarkCarModule {

    public MarkCarModule() {
    }

    @Provides
    @CarScope
    Engine provideEngine() {
        return new Engine("gear");
    }
}

同时还需要使用@Scope去标注注入器Compoent

@CarScope
@Component(modules = {MarkCarModule.class})
public interface CarComponent {
    void inject(Car car);
}

为了便于测试我们对Car和Engine类做了一些改造:

public class Car {

    @Inject Engine engineA;
    @Inject Engine engineB;

    public Car() {
        DaggerCarComponent.builder()
                .markCarModule(new MarkCarModule())
                .build().inject(this);
    }
}
public class Engine {

    private String gear;

    public Engine(String gear){
        System.out.println("Create Engine");
        this.gear = gear;
    }
}

如果我们不适用@Scope,上面的代码会实例化两次Engine类,因此会有两次"Create Engine"输出。现在我们在有@Scope的情况测试下劳动成果:

public static void main(String[] args) {
    Car car = new Car();

    System.out.println(car.engineA.hashCode());
    System.out.println(car.engineB.hashCode());
}

输出

Create Engine

bingo!我们确实通过@Scope实现了局部的单例。

Dagger2原理分析

前面啰里啰嗦的介绍了Dagger2的基本使用,接下来我们再分析分析实现原理。这里不会分析Dagger2根据注解生成各种代码的原理,关于Java注解以后有机会再写一篇文章来介绍。后面主要分析的是Dagger2生成的各种类如何帮我们实现依赖注入,为了便于理解我这里选了前面相对简单的案例B来做分析。

Dagger2编译期生成的代码位于build/generated/source/apt/debug/your package name/下面:
[图片上传失败...(image-75dbbe-1531401791047)]

首先我们看看Dagger2依据依赖提供方MarkCarModule生成的对应工厂类MarkCarModule_ProvideEngineFactory。为了方便大家理解对比,后面我一律会把自己写的类和Dagger2生成的类一并放出来。

/**
* 我们自己的类
*/
@Module
public class MarkCarModule {

    public MarkCarModule(){ }

    @Provides Engine provideEngine(){
        return new Engine("gear");
    }
}

/**
* Dagger2生成的工厂类
*/
public final class MarkCarModule_ProvideEngineFactory implements Factory<Engine> {
  private final MarkCarModule module;

  public MarkCarModule_ProvideEngineFactory(MarkCarModule module) {
    assert module != null;
    this.module = module;
  }

  @Override
  public Engine get() {
    return Preconditions.checkNotNull(
        module.provideEngine(), "Cannot return null from a non-@Nullable @Provides method");
  }

  public static Factory<Engine> create(MarkCarModule module) {
    return new MarkCarModule_ProvideEngineFactory(module);
  }

  /** Proxies {@link MarkCarModule#provideEngine()}. */
  public static Engine proxyProvideEngine(MarkCarModule instance) {
    return instance.provideEngine();
  }
}

我们可以看到MarkCarModule_ProvideEngineFactory中的get()调用了MarkCarModuleprovideEngine()方法来获取我们需要的依赖EngineMarkCarModule_ProvideEngineFactory的实例化有crate()创建,并且MarkCarModule的实例也是通过create()方法传进来的。那么这个create()一定会在哪里调用的,我们接着往下看。

前面提到@Component是依赖提供方(MarkCarModule)和依赖需求方(Car)之前的桥梁,那我看看Dagger2是如何通过CarComponent将两者联系起来的。

/**
* 我们自己的类
*/
@Component(modules = {MarkCarModule.class})
public interface CarComponent {

    void inject(Car car);
}

/**
* Dagger2生成的CarComponent实现类
*/
public final class DaggerCarComponent implements CarComponent {
  private Provider<Engine> provideEngineProvider;

  private MembersInjector<Car> carMembersInjector;

  private DaggerCarComponent(Builder builder) {
    assert builder != null;
    initialize(builder);
  }

  public static Builder builder() {
    return new Builder();
  }

  public static CarComponent create() {
    return builder().build();
  }

  @SuppressWarnings("unchecked")
  private void initialize(final Builder builder) {

    this.provideEngineProvider = MarkCarModule_ProvideEngineFactory.create(builder.markCarModule);

    this.carMembersInjector = Car_MembersInjector.create(provideEngineProvider);
  }

  @Override
  public void inject(Car car) {
    carMembersInjector.injectMembers(car);
  }

  public static final class Builder {
    private MarkCarModule markCarModule;

    private Builder() {}

    public CarComponent build() {
      if (markCarModule == null) {
        this.markCarModule = new MarkCarModule();
      }
      return new DaggerCarComponent(this);
    }

    public Builder markCarModule(MarkCarModule markCarModule) {
      this.markCarModule = Preconditions.checkNotNull(markCarModule);
      return this;
    }
  }
}

通过上面的代码我们看到Dagger2依据CarComponent接口生成了实现类DaggerCarComponent(没错这正是我们在Car的构造函数中使用DaggerCarComponent)。DaggerCarComponent在build的时候实例化了DaggerCarComponent对象,并首先调用MarkCarModule_ProvideEngineFactory.create(builder.markCarModule)始化了provideEngineProvider变量,接着调用Car_MembersInjector.create(provideEngineProvider)初始化了carMembersInjector变量。当我们手动在Car类的构造函数中调用inject(Car car)方法时会执行carMembersInjector.injectMembers(car)。所以接下来我们要看看Car_MembersInjector的实现。

public final class Car_MembersInjector implements MembersInjector<Car> {
  private final Provider<Engine> engineProvider;

  public Car_MembersInjector(Provider<Engine> engineProvider) {
    assert engineProvider != null;
    this.engineProvider = engineProvider;
  }

  public static MembersInjector<Car> create(Provider<Engine> engineProvider) {
    return new Car_MembersInjector(engineProvider);
  }

  @Override
  public void injectMembers(Car instance) {
    if (instance == null) {
      throw new NullPointerException("Cannot inject members into a null reference");
    }
    instance.engine = engineProvider.get();
  }

  public static void injectEngine(Car instance, Provider<Engine> engineProvider) {
    instance.engine = engineProvider.get();
  }
}
来自:https://segmentfault.com/a/1190000008016507

Car_MembersInjector中的create()用于实例化自己,这个方法前面我们看到是在DaggerCarComponent中调用的。injectMembers(Car instance)engineProvider.get()的返回值赋给了依赖需求方Car的engine变量,而engineProvider.get()正是本节一开始我们提到的MarkCarModule_ProvideEngineFactory中的get()方法。至此整个依赖注入的流程就完成了。更复杂的应用场景会生成更加复杂的代码,但原理都和前面分析的大同小异。

总结

这篇文章只是通过一些简单的例子介绍了Dagger2的相关概念及使用,实际项目中的应用远比这里的例子要复杂。关于Dagger2在实际项目中的应用可以参照这个开源项目 https://github.com/BaronZ88/MinimalistWeather(项目采用MVP架构,其中View层和Presenter层的解耦就是通过Dagger2来实现的)。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,013评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,205评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,370评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,168评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,153评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,954评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,271评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,916评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,382评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,877评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,989评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,624评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,209评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,199评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,418评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,401评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,700评论 2 345

推荐阅读更多精彩内容