Dagger2初探

作者: 皮球二二 | 来源:发表于2016-07-20 22:58 被阅读292次

    在你逛Github的时候,如果遇到一些大型的开源项目,恐怕最眼熟的几个关键字就是RxJava、Retrofit、MVP、Dagger2、RetroLambda等等。在之前的文章中,我们先后学习过Rxjava、Retrofit以及Lambda表达式的使用,那么今天我们就来看看Dagger2是什么玩意

    Dagger2是一款使用在Java和Android上的依赖注入的一个类库,目前Dagger有两个分支,一个由Square维护,一个为Google在前者的基础上开出的分支,即Dagger2

    那么问题来了,你说了这么多基本概念,我还是一头雾水,这个到底能给我带来什么好处呢?

    回顾一下我们之前是怎么初始化一个对象的

    public class ModelD {
        int a;
        int b;
        public ModelD() {
        }
        public int getA() {
            return a;
        }
        public void setA(int a) {
            this.a = a;
        }
        public int getB() {
            return b;
        }
        public void setB(int b) {
            this.b = b;
        }
    }
    

    这是一个非常标准的对象。使用也是直接声明后初始化,进行相应set/get操作

    ModelD modelD;
    
    modelD=new ModelD();
    modelD.setA(10);
    modelD.setB(20);
    Log.d("MainActivity", modelD.getA() + " " + modelD.getB());
    

    这样就是一个典型的用到哪里new到哪里的用例。有没有觉得好心疼?每次检查代码的时候,都得翻遍代码去找哪里new的?为了解决这个问题,统一化管理各种Model,从现在开始,我们就来学习Dagger2

    环境搭建

    1. 在你项目的build.gradle下添加
    buildscript {
        ......
        dependencies {
            ......
            classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
        }
    }
    
    1. 在你主工程的build.gradle下添加
    apply plugin: 'com.neenbedankt.android-apt'
    ......
    dependencies {
        ......
        compile 'com.google.dagger:dagger:2.5'
        apt "com.google.dagger:dagger-compiler:2.5"
        provided 'javax.annotation:javax.annotation-api:1.2'
    }
    

    这样就完成了环境搭建了

    @Inject

    我们先简单接触一下Dagger2,看看他到底有什么功能

    Inject即javax.inject.Inject。当你的类需要自动实例化的功能,那么就得在你的构造方法中通过@Inject注解来告诉Dagger2如何实例化。在完成构造方法的注解之后,后续就需要对成员变量进行注解以告诉Dagger2哪个对象需要实例化

    那么我们如何使用Dagger2去对之前初始化过程进行改造呢?

    public class ModelD {
        ......
        @Inject
        public ModelD() {
        }
        ......
    }
    
    @Inject
    ModelD modelD;
    

    注意这边modelD对象不可以是private

    初步改造完成,但是这样还是无法运行的,我们还需要一个桥梁去连接这两部分,谁去做这个连接器呢?它就是Component

    @Component

    刚说了Component是一个连接器,他通过查找目标类中用Inject注解标注的成员变量,查找到相应的成员变量后,接着查找该成员变量所在的类对应的用Inject标注的构造函数(这时候就发生联系了),剩下的工作就是初始化该变量的实例并把实例进行赋值。
    Component注解的类只能是接口或抽象类,每个@Component必须至少有一个抽象方法,他们的名字可以是任意的,但是格式必须符合provision或者members-injection,后面还有一种@Subcomponent时需要的一种接口方法,总共就这三种。provision在下期Subcomponent的介绍中会提及,members-injection就是我们之前写的inject方法

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

    这里强调说明下,inject中的内容代表真正消耗依赖的类型,这里是MainActivity。如果你在这边写其他对象,或者重复某个对象,在编译过程中都会出错的

    编译之后Dagger2会按照上面接口规定的协议生成一个实现类,通过编译我们可以生成以Dagger为前缀的类,提供builder()来生成实例

    public final class DaggerMyComponent implements MyComponent {
      private MembersInjector<MainActivity> mainActivityMembersInjector;
      private DaggerMyComponent(Builder builder) {
        assert builder != null;
        initialize(builder);
      }
      public static Builder builder() {
        return new Builder();
      }
      public static MyComponent create() {
        return builder().build();
      }
      @SuppressWarnings("unchecked")
      private void initialize(final Builder builder) {
        this.mainActivityMembersInjector = MainActivity_MembersInjector.create(ModelD_Factory.create());
      }
      @Override
      public void inject(MainActivity activity) {
        mainActivityMembersInjector.injectMembers(activity);
      }
      public static final class Builder {
        private Builder() {}
        public MyComponent build() {
          return new DaggerMyComponent(this);
        }
      }
    }
    

    这里的injectMembers就是将成员变量注入。

    这样就可以直接通过无参构造方法去实例化一个对象了

    DaggerMyComponent.builder().build().inject(this);
    

    但是请注意,这种写法不适用于有参构造方法,那么有参构造方法需要怎么实现呢?我们来看看@Module与@Provides

    @Modules

    Modules相当于一个管理集合,他负责集中创建欲初始化的类的对象,简单的说它就是一个依赖提供者。

    直接在构造方法中写Inject注解也是很麻烦的一件事:

    1. 有时候我们不能使用Inject去注解一个构造方法,比如jar包里面的
    2. 只可以在一个构造方法中添加Inject注解,不然Dagger2也不知道你初始化的到底是哪个构造方法
    3. 接口不能使用,谁知道你接口是怎么实现的

    所以我们这里就需要使用Provides去注解一个方法来满足依赖。方法的返回类型就是依赖要满足的类型。

    @Provides

    这是添加该注解的方法,表明用来初始化某个实例对象。

    我们将刚才ModelD的初始化用Modules跟Provides来实现以下

    public class ModelD {
        int a;
        int b;
        public ModelD(int a, int b) {
            this.a=a;
            this.b=b;
        }
    }
    
    @Module
    public class TotalModule {
        int a;
        int b;
        public TotalModule(int a, int b) {
            this.a=a;
            this.b=b;
        }
        @Provides
        public ModelD providesModelD() {
            return new ModelD(a, b);
        }
    }
    

    通过对TotalModule的初始化,将变量用于ModelD的初始化。注意这边Module的类名以及Provides注解的方名的命名规则,尽量做的规范起来

    请注意,如果Provides所注解的方法有入参,这个参数一定要被Dagger得到(通过其他的provider方法或者@Inject注解的构造方法获得)。并且方法的返回值类型必须唯一,不能重复

    剩下就是用Component去关联起来,不然谁知道这个Module?

    @Component(modules = TotalModule.class)
    public interface TotalComponent {
        void inject(MainActivity activity);
    }
    

    有个地方要注意,我们这边TotalModule显示声明了一个带2个参数的构造方法,所以在编译时注解生成的DaggerTotalComponent对象的内部类Builder跟之前就有所区别了,它强制要求我们实现TotalModule

    public static final class Builder {
      private TotalModule totalModule;
      private Builder() {}
      public TotalComponent build() {
        if (totalModule == null) {
          throw new IllegalStateException(TotalModule.class.getCanonicalName() + " must be set");
        }
        return new DaggerTotalComponent(this);
      }
      public Builder totalModule(TotalModule totalModule) {
        this.totalModule = Preconditions.checkNotNull(totalModule);
        return this;
      }
    }
    

    使用时候跟之前类似

    DaggerTotalComponent.builder().totalModule(new TotalModule(2, 3)).build().inject(this);
    

    其实还有一种写法。可能你觉得刚才的写法实在是麻烦,每次TotalModule都初始化相同的数据,不需要这么麻烦

    @Module
    public class TotalModule2 {
        @Provides
        public ModelD providesModelD(int a, int b) {
            return new ModelD(a, b);
        }
        @Provides
        public int providesInteger() {
            return 1;
        }
    }
    

    @Module的优先级高于@Inject。优先从provides里面获取构造方法,如果找不到,再从Inject里面获取,如果什么都找不到,就报错了

    到底为止,就是Dagger2的典型使用方式了

    参考文章

    Dagger2使用,详细解读和从Dagger1迁移的方法介绍
    Dagger2使用详解
    Android常用开源工具(2)-Dagger2进阶

    本文演示示例已经上传到Github

    首次接触Dagger2框架,个人觉得相对于其他框架来说,这玩意还是有些难度的,所以错误之处请多多指点,我也会在空闲时间外根据自己的理解对文章多多改进,谢谢支持

    相关文章

      网友评论

        本文标题:Dagger2初探

        本文链接:https://www.haomeiwen.com/subject/edhrjttx.html