Dagger2笔记--讲道理我差点就放弃了

为什么要使用Dagger2呢

其实是自己立的flag , 哭着也要学完(这是三个月之前的flag)
曾经我哭着看老大些的dagger2代码问他 , 你丫的直接new不就行了么骚什么骚。直到我后来自己封装网络框架的时候发现,dagger2是真的舒服,直接省去了new一大坨东西的烦恼,而且对应不同的场景也能快速做出适应,一个@Inject就可以用到你需要用的东西,而针对目标对象的修改是和调用完全没有关系的。你想怎么改怎么改,反正我一个inject就能用。

从最简单的例子来入手

别上来就被一大堆眼花缭乱的注解吓到了,咱们由浅入深来看。
需求: 调用一个对象,省略实例化的过程
首先创建一个对象Student,提供一个方法输出log。

public class Student {

    Student() {
    }

    public void syso() {
        Log.e("syso", "syso: ");
    }
}

接下来使用dagger来辅助我们实例化对象。首先我们要接触两个注解

  1. @Inject
  2. @Component

怎么用啊这玩意儿,用在哪里呢?
直接看看Inject的实现,这是一个注解接口,代码也非常少

@Target({ METHOD, CONSTRUCTOR, FIELD })
@Retention(RUNTIME)
@Documented
public @interface Inject {}

说明了Annotation所修饰的对象范围。这里标注了范围为:方法,构造,变量
Rentention:在运行时有效(即运行时保留)
这就说明我们以后再使用@Inject的时候,可能会出现在三个地方。
首先,我们对Student进行改造,在构造方法标上@inject的注解

public class Student {
    @Inject
    Student() {
    }

    public void syso() {
        Log.e("syso", "syso: ");
    }
}

然后我们看看Component的代码

@Retention(RUNTIME) // Allows runtimes to have specialized behavior interoperating with Dagger.
@Target(TYPE)
@Documented
public @interface Component {

  Class[] modules() default {};
  Class[] dependencies() default {};

  @Target(TYPE)
  @Documented
  @interface Builder {}
}

首先看Target,用于描述类、接口(包括注解类型) 或enum声明。
然后内部还有三个方法,这个以后再说。
然后我们创建一个自定义的StuComponent用来作为注解接口,同时加注解

@Component
public interface StuComponent {
}

有了这些准备工作之后,我们首先构建一次工程make project,让apt自动生成相关的代码,然后编写测试类College,同时在StuComponent中编写任意方法返回void,传入参数为测试类。

@Component
public interface StuComponent {
    void inject(College college);
}

public class College {
    @Inject
    Student student;

    public College() {
        // DaggerStuComponent为make之后自动生成的代码,build之后调用注册的方法,将自己传进去。
        DaggerStuComponent.builder().build().inject(this);
    }

    public void test(){
        student.syso();
    }
}

最后直接调用College的test方法,即可拿到输出结果


是不是感觉一愣一愣的,这里来小结一下:

  1. 首先对于想要实例化的对象,在构造函数处标注@Inject
  2. 创建接口并标注@Component
  3. 构建工程(这个过程会自动生成代码)
  4. 在Component中编写范围值为void,传入参数为A的函数,A为需要使用对象的类
  5. 在A中使用Dagger,同时直接@Inject声明对象,即可使用了

全程没有通过实例化目标对象,但真正的拿到了对象的实例,这就是Dagger2的最基本的使用了。接下来我们看看在Make的时候都发生了什么事。
首先回到College类中, 查看College在哪里被使用了, 我们发现了一个叫College_MembersInjector的类 , 点开该类,我们可以找到一个叫injectMembers的方法,在这个方法中,判空之后使用instance.student = studentProvider.get();来对student赋值。那么此时的student是谁呢,继续跟踪student这个变量,发现我们回到了College中的被标记的变量。这就说明@Inject标记的变量不能声明为private,否则Injector无法访问。
接下来看Student这个类的构造函数,可以跟踪到一个枚举类Student_Factory

public enum Student_Factory implements Factory {
  INSTANCE;

  @Override
  public Student get() {
    return new Student();
  }

  public static Factory create() {
    return INSTANCE;
  }
}

一看名字就知道是个工厂类,在枚举的get方法实例化对象。好了,找到对象的创建以及使用的地方,那么这两者是如何同时工作的呢?回到我们创建的Component中,跟踪接口的实现:

public final class DaggerStuComponent implements StuComponent {
  private MembersInjector collegeMembersInjector;

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

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

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

  @SuppressWarnings("unchecked")
  private void initialize(final Builder builder) {
    this.collegeMembersInjector = College_MembersInjector.create(Student_Factory.create());
  }

  @Override
  public void inject(College college) {
    collegeMembersInjector.injectMembers(college);
  }

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

    public StuComponent build() {
      return new DaggerStuComponent(this);
    }
  }
}

这个类不就是我们在College中使用Dagger的类么,来看看我们怎么用的
DaggerStuComponent.builder().build().inject(this);据此,我们来跟一边代码:

首先Builder,build()方法,构建了一个DaggerStuComponent的实例,那么在DaggerStuComponent的构造方法又调用了initialize,然后又this.collegeMembersInjector = College_MembersInjector.create(Student_Factory.create());穿件了College_MembersInjector,并传入Student_Factory的单例,最终实例化MembersInjector这个注解器。构建完毕后又调用了inject方法,继续跟进,发现最后回到了College_MembersInjector的injectMembers方法。这个方法是不是有点眼熟?之前我们在上文已经见过了,这就是最终实例化对象的地方。
饶了一大圈,我们简单概括一下:
inject标记在构造函数上,用来声明可以被引用;标记在变量上意味着引用,Component意味着实例化与调用之间的桥梁。具体的构建方法类似于工厂模式。

Dagger2笔记--讲道理我差点就放弃了_第1张图片


第二个例子:无法修改构造函数的对象

比如我们需要使用Retrofit,哎呀,这是库啊,源码是反编译后的,不能修改,这种情况下能不能使用Dagger呢?我们就需要使用一些新的注解了。
需求:调用一个对象,省略实例化的过程,且不修改构造函数

  1. 创建一个对象,创建好就不允许修改了
public class Car {
    public Car() {
    }
    public void log(){
        Log.e("Car", "Car: ");
    }
}
  1. 创建一个带Moudle注解的类,并提供Car的实例化:
@Module
public class CarMoudle {

    @Provides
    Car providerCar() {
        return new Car();
    }
}
  1. 创建Component,用来搭建实例化与调用的桥梁,并制定Moudle
@Component(modules = CarMoudle.class)
public interface CarComponent {
    void inject(CarTest car);
}
  1. 测试,同样需要先用建造者模式构建Dagger,然后就可以直接inject了
public class CarTest {
    @Inject Car car;

    public void test(){
        DaggerCarComponent.builder().carMoudle(new CarMoudle()).build().inject(this);
        car.log();
    }
}

运行结果如图所示



这个过程同样涉及到了三个类。首先来看Moudle类

  1. Moudle注解,细节上面说过了。这个注解可以给不能修改构造函数的类提供依赖
  2. @Providers同样类似,内部提供枚举。被Providers标记的方法返回的对象,就是需要依赖的对象的实例
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Module {
  Class[] includes() default {};
}
  1. 看Component类同样作为桥梁,和上面类似但是有不太一样,单独摘出来
    @Component(modules = CarMoudle.class)
    在上一个demo中,我们提到了Component可以接收以下的参数
  • modules: 指定对应的modules
  • dependencies: 指定继承

这里我们指定了modules = CarMoudle.class

  1. 看测试类的Dagger的使用也有一些不一样,因为DaggerCarComponent是基于Component来生成的,但是使用到modules,所以在使用时额外要配置DaggerCarComponent.builder().carMoudle(new CarMoudle()).build().inject(this);

总结下使用方法:

  1. 新建Module类,标记@Module注解,同时编写返回目标对象的方法,标记@Providers注解
  2. 创建Component接口,标记@Component,指定参数modules
  3. 在测试类中构建Dagger,一定要传入module,然后注入。之后@Inject需要调用的对象
  4. 在Component中编写注入方法

接下来我们分析过程中的代码。
首先是CarModule类(我们无法进入Car类内部,所以从这个类开始分析),在这个类中我们跟踪到了相关的Factory类

public final class CarMoudle_ProviderCarFactory implements Factory {
  private final CarMoudle module;

  public CarMoudle_ProviderCarFactory(CarMoudle module) {
    assert module != null;
    this.module = module;
  }

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

  public static Factory create(CarMoudle module) {
    return new CarMoudle_ProviderCarFactory(module);
  }
}

构造函数中实例化Moudle,然后get方法返回了Car实例,与之前的类似。
然后跟进CarComponent的实现类:

public final class DaggerCarComponent implements CarComponent {
  private Provider providerCarProvider;

  private MembersInjector carTestMembersInjector;

  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.providerCarProvider = CarMoudle_ProviderCarFactory.create(builder.carMoudle);

    this.carTestMembersInjector = CarTest_MembersInjector.create(providerCarProvider);
  }

  @Override
  public void inject(CarTest car) {
    carTestMembersInjector.injectMembers(car);
  }

  public static final class Builder {
    private CarMoudle carMoudle;

    private Builder() {}

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

    public Builder carMoudle(CarMoudle carMoudle) {
      this.carMoudle = Preconditions.checkNotNull(carMoudle);
      return this;
    }
  }
}

同样,我们在测试类中使用的方法正式这个类的一些方法。
DaggerCarComponent.builder().carMoudle(new CarMoudle()).build().inject(this);不一样的是这里传入了一个Module,最终还是通过build方法创建了CarComponent的对象。然后注入,调用了CarTest_MembersInjector的injectMembers方法注入。注入之后,通过instance.car = carProvider.get();将实例化的值赋值给测试类的对象。
基本上和@Inject的过程是一模一样的。

Dagger2笔记--讲道理我差点就放弃了_第2张图片

你可能感兴趣的:(Dagger2笔记--讲道理我差点就放弃了)