Dagger2-终于学会了,还好没放弃(1)

MVP + RxJava + Retrofit + Dagger2 现在已经成了Android开发的主流框架,为了避免落伍,我也开始了这个组合框架的学习,力求摆脱以前的那种简单粗暴的分包模式。因而也开始了 Dagger2 的学习之路。这几天断断续续的也看了不少文章,其中固然有不少优秀的文章,可是慢慢的以为并无哪一篇文章,是彻底的能让人只须要看一篇就能彻底让一我的从0开始入门的,Dagger2这个框架学习起来仍是比较费劲的,每每是搜了不少文章,综合起来才慢慢的能把不少东西搞懂,这就使得不少人还没入门就屡次放弃了(说实话,我也放弃了不少次)。在这里,我想力求作到从一个“白痴”的角度(我是不会认可是个人)进行讲解,以问题为导向来说解,不少时候咱们以为写博客的人虽然写了不少,以为很牛逼,但可恨的是,咱们仍是不懂。其实这并非咱们的错,只是做者忘记了当初本身是怎么过来的,各类问题是怎么一步步被提出来的,结果就是变成了教科书式的讲解。下面就让咱们一切从头来过,这一次,决不放弃。html

什么是依赖注入,为何咱们须要Dagger2

这里,我就不在粘贴那些很官方的描述了,直接大白话。“依赖注入”,从这个词来讲,就是 “依赖” 和 “注入” 两个部分嘛。所谓“依赖”,和咱们生活中理解的同样,好比说汽车和汽油的关系,汽车只有在有汽油存在的状况下才有意义,严重依赖汽油。而“注入”,很容易让咱们想到注射器,将一种液体注入到另外一种容器中,这里的“注入”也是同样的,只不过是讲对象绑定到另外一个类中而已(简单点来说就是经过引用进行赋值)。java

那为何须要依赖注入呢?其实就是为了简化无用操做,提升效率,而且对代码进行解耦,使得需求或者一个地方的代码变更,尽量小的引发其余部分代码的修改。咱们以箱子和箱子的水果这个场景为例,进行讲解。android

首先建立一个Box类,在其中又依赖了两个类:Apple 和 Banana。编程

public class Box {
    Apple mApple;

    public Box(Apple apple){
        mApple = apple;
    }
}

箱子里装有苹果。下面看具体使用:设计模式

public class MainActivity extends AppCompatActivity {

    Apple mApple;
    Box mBox;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mApple = new Apple(); // 先建立 Apple 对象

        mBox = new Box(mApple, mBanana); // 将建立好了的 Apple 对象传入 Box 对象的构造函数中
    }

能够看到,咱们要向获得一个Box对象,必需要先手动生成一个Apple对象和一个Banana对象,试想一下,若是在之后,咱们修改了Apple类,在其中添加了对苹果的描述,改变了构造方法,好比变为:app

public class Apple {

    public Apple(String desctiption){
    
        Log.i("qc","苹果的描述为: ");
    }

}

那咱们就须要再去修改 MainActivity.java 类,显然按,这种编程习惯是很差的。咱们能不能经过其余方法,来下降这种影响呢?其实,在编程中咱们不少时候是用到过的,只是咱们没意识到,想一想,在列表加载中,为适配器设置数据、为列表项加载头布局和尾布局的时候你是怎么设置的?Bingo,经过setXXX(...)方法,好比,在Adapter中设置一个public方法来设置数据集:框架

public void setData(List data){
        ...
    }

代码编写的多了,就会更加的接近设计模式、编程思想的本质,毕竟设计模式源于实践而且指导实践的,咱们可能不再愿意去直接在建立Adapter的构造函数中直接传入数据集了。
若是以为这点我说的不太清楚的话,能够参考这篇文章:使用Dagger2前你必须了解的一些设计原则
其实,Dagger2 的思想,跟这个就很像,都是为了解耦。使用 Dagger2 ,这些setXXX(...)的方法,彻底就不须要咱们本身去作了。ide

这里再补充一点,若是你熟悉MVP的话,就很容易能感觉到 Dagger2 的魅力,MVP 模式中,View和 Presenter 互相持有对方的引用,而 Presenter 同时又持有 Model 的引用,这样,就形成了View和 Presenter 之间的耦合性比较大,使用 Dagger2 能够减弱这种耦合,直接将 Presenter 经过依赖注入的方式注入到 View 中(例如Activity),固然,若是你还不了解MVP的话,如今学习这篇文章,千万别再跑去看MVP,而丢了主次,彻底不须要,那只是 Dagger2 的一个使用场景。函数

Dagger2学习须要的基础:注解

在学习Dagger2以前,你必定要了解的东西就是Java中的注解,能够说若是不懂注解的话,学习Dagger2 是很是痛苦的一件事,甚至彻底不知道整个过程是怎么进行的。关于注解,并不难,一两篇文章就能够搞懂,不会花费你不少时间的,能够参考这篇文章:JAVA 注解的几大做用及使用方法详解布局

之前咱们每每是在须要的地方手动的去经过 new 来生成对象,对象的生成和对象的使用是在同一个地方,而 Dagger2 将对象的是生成和其使用分开。说白了,Dagger2 的整个过程就两个:

  1. 建立并初始化对象(被依赖的对象,好比 Apple)

  2. 将初始化好的对象经过“中间人”放到须要它的那个对象中(目标类,或者成为容器,好比 Box)

那么就让咱们直接经过具体的例子来进行讲解,让你们更直观的感觉到整个过程。

Dagger2 具体使用

1.使用Dagger2 须要的相关配置

如今Dagger2抛弃了以前采用的apt自动生成代码的方式,而采用annotationProcessor,于是
若是你使用的Gradle插件版本在2.2及以上,只须要在应用对应的build.gradle文件中添加如下依赖便可:

dependencies {

    ...
    ...
    
    // Dagger2相关依赖
    compile 'com.google.dagger:dagger:2.11-rc2'
    annotationProcessor 'com.google.dagger:dagger-compiler:2.11-rc2'
}

若是用的是比较旧的版本,能够参考GitHub上项目帮助文档:https://bitbucket.org/hvisser...

2.如何生成对象

既然是依赖注入,咱们首先须要作的就是把对象生产出来。那怎么生成一个对象呢,很容易想到在构造函数前加一个 new ,如:new Apple(),可是既然 Dagger2 能让它自动生成,咱们固然不须要本身动手,只须要作某种标记,好比能够直接加上@Inject注解。
这里咱们首先提出来,生成所需对象的方式有两种:

  1. @Inject 注解提供方式:在所需类的构造函数中直接加上 @Inject 注解

  2. @Module 注解提供方式:经过新建一个专门的提供这类来提供类实例,而后在类名上面添加 @Module 注解,在类中本身定义方法,手动经过 new 来进行建立,这种主要是针对第三方库中,咱们没法直接在构造函数中添加 @Inject 注解的状况。

3.具体实施步骤

这里,咱们先针对第一种状况进行讲解,也就是 @Inject 注解,先跑通整个流程
下面一步步讲解:

第一步,很简单:就是在Apple类的构造函数上添加 @Inject 注解标记

public class Apple {
    
   @Inject // 注意此处的标记是加在构造函数上面的,这个是无参构造函数,有参构造函数的状况下以后会讲
   public Apple(){
       
   }

   public String getDescription(){
       return "这个苹果然好吃";
   }

}

这样,咱们就完成了提供者端的操做了,很简单吧,仅仅只是在构造函数上面添加了一个 @Inject 注解。

这一步,是告诉编译器及其余处理程序,咱们须要经过此处来生成对象,从而供之后进行依赖注入。

第二步,在目标类中的对应成员变量上也添加相同的注解 @Inject (注意,在目标类中这个注解始终是 @Inject ,而不会由于以后使用 @Module 这种方式而改变)

public class MainActivity extends AppCompatActivity {

    @Inject
    Apple mApple; // 在这里,咱们添加了@Inject 注解,告诉处理程序,咱们须要将对象注入到此处,给此引用赋值

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
    }
    
}

在这一步,咱们经过在目标类中属性上添加 @Inject 注解,来告诉编译器和其余代码自动生成程序,咱们须要将以前生成的对象注入到这里(赋值给这里的引用)。
注意,属性的访问修饰符必定不能是private类型的,不然没法经过Dagger2进行依赖注入,由于咱们实际上是经过传入引用,而后经过引用来调用其中的属性,一旦声明为private,咱们天然没法经过mainActivity.mApple这种方式来进行赋值操做了。
至此,咱们就完成了最基本的两步了,咱们知道了从哪里生成对象,也知道了生成以后的对象须要在哪里使用,可是,如何将两者联系起来呢,也就是说,我如何经过某种方式,将生成的对象送达到须要的地方呢?这就须要一个“中间人”来起到桥梁的做用,让两者产生实质上的关联。这就是接下来要讲到的 @Component 注解了。

第三步,经过 @Component 进行牵线搭桥
咱们最好创建一个包,名叫component,以方便管理。在该包下新建一个接口类,建议统一以Component做为后缀,好比下面的 MainComponent.java 。

@Component
public interface MainComponent {
    void inject(MainActivity activity);
}

咱们在该接口上面添加了 @Component 注解,同时定义了一个抽象方法:void inject(MainActivity activity);
注意,这个方法,返回值是void类型的,后面的方法名能够随便起,括号内的参数是咱们须要将对象注入的类,必定要是具体的类,而不能是其父类。其实,以后咱们使用的时候,就是将对应的Activity传进去,而后经过引用调用该对象的属性进行赋值,思想很简单。这里要强调一点,这里并非必定要求是void类型的,能够有具体的返回值,这种状况咱们会在以后具体讨论,这里先经过这个void简单类型来学习,避免复杂化。函数名也通常先命名为inject,便于理解。

其实,写到这里,咱们的整个过程基本上就完成了,下面只须要经过代码,进行实际的注入就行了。可能你会问,这就完成了?咱们上面只是定义了一个接口,并无具体的实现啊,怎么就能注入呢?接下来,咱们就来见证奇迹的时刻把,咱们把项目编译一下,或者直接点击AS上的绿色的锤子图标:

clipboard.png
而后你打开下图的目录,就会发现会自动生成一些类。

clipboard.png

这就是咱们在build.gradley依赖中添加的注解处理器(AnnotationProcess)给咱们自动生成的。以前是没有apt这整个目录的,这里面咱们能够看到,它也按照咱们分的包的结构来生成的,好比“component”包。这里面如今对咱们来说最重要的也是直接跟咱们发生关系的就是DaggerMainComponent.java这个类,咱们进行具体的依赖注入其实就是使用的这个类,这个类是Dagger2框架自动为咱们生成的,能够看到,它是以Dagger开头的。当咱们建立了一个Component,好比MainComponent以后,系统会自动为咱们生成一个以Dagger为前缀,后面跟上咱们的Component名的一个具体实现类。先知足一下你的好奇心,咱们点进去,看一下这个类。

public final class DaggerMainComponent implements MainComponent {
  private MembersInjector<MainActivity> mainActivityMembersInjector;

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

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

  public static MainComponent create() {
    return new Builder().build();
  }

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

    this.mainActivityMembersInjector = MainActivity_MembersInjector.create(Apple_Factory.create());
  }

  // 看到了吧,这个就是咱们在以前定义的接口,此处系统自动为咱们进行了实现
  @Override
  public void inject(MainActivity activity) {
    mainActivityMembersInjector.injectMembers(activity);
  }

  public static final class Builder {
    private Builder() {}

    public MainComponent build() {
      return new DaggerMainComponent(this);
    }
  }
}

能够看到,它实现了咱们以前写的MainComponent接口,里面也对咱们以前定义的抽象方法进行了实现。

@Override
  public void inject(MainActivity activity) {
    mainActivityMembersInjector.injectMembers(activity);
  }

到这里知道为啥我说名字能够随便命名了吧。

好啦,看到这里就够了,千万别陷进去,一下子咱们还会回来继续分析这个类的,千万别以为我是在浅尝辄止啊。
仍是再提醒一下,编写完以后,必定要编译一下项目,不然这些不会生成的,下面你也无法用Dagger***Component来进行注入了,会提醒你找不到该类。

第四步,也是最后一步,使用Dagger***Component进行依赖注入
咱们只须要在咱们的目标类中,调用注入代码就能够了,以下代码:

public class MainActivity extends AppCompatActivity {

    @Inject
    Apple mApple;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 将依赖注入到该Activity中
        DaggerMainComponent
                .builder()
                .build()
                .inject(this);
                
        // 打印测试消息
        Timber.i(mApple.getDescription());
        
    }
}

运行项目,能够看到,在日志中已经打印出来了咱们Apple类中的信息(这里使用的是JakeWharton大神的日志打印框架,你直接用 Log.i 打印就好)。

clipboard.png

好了,到这里,咱们算是将Dagger2 的一个流程整个跑完了,但这只是一种注入的方式,也就是直接经过在类的构造函数上面添加@Inject注解来完成依赖注入,在下一篇文章中我将会为你们讲解两一种提供对象的方式:@Module

相关文章
相关标签/搜索