美文网首页
Dagger2 基本使用

Dagger2 基本使用

作者: 农夫龙泉 | 来源:发表于2019-10-11 10:23 被阅读0次

前言

在熟悉Dagger2 之前先回顾下依赖注入的概念(Dependency injection),简称DI ,DI是控制反转的一种技术实现, 例如:客户端代码client 调用一个服务service, service是客户端代码的一部分,客户端无需实例化这个service 而是将service 的构建交给外部类来完成并且设置给client,不允许client代码擅自创建需要注入的对象service,client 无需关注service是如何构建的,是哪个service实例, 而是只需要关注如何使用service的接口,这样就做到了使用和构建分离, 在往后中如果service 的实现发生改变也不会影响到client代码。

Dagger

Dagger 就是一个DI框架,可用于Java 和 Android工程中,在编译期Dagger为我们生成一些代码用于组织依赖关系。在Android中使用Dagger 需要以下的依赖。

配置:

    implementation 'com.google.dagger:dagger-android:2.16'
    annotationProcessor 'com.google.dagger:dagger-compiler:2.16'
    // if you use the support libraries 
    implementation 'com.google.dagger:dagger-android-support:2.16'
    annotationProcessor 'com.google.dagger:dagger-android-processor:2.16'

那就开始吧

瞎编一个例子吧, 有一天去电脑城买个电脑,买一个品牌机也就是已经给我装好的那种,电脑的组件用的什么其实卖家最清楚,我也懒得看,就把电脑抱回来了能打LOL就成。
电脑都需要哪些组件? 先看下吧:

public class Computer {

  @Inject
  Cpu cpu;

  @Inject
  GraphicCard graphicCard;

  @Inject
  Monitor monitor;

  @Inject
  public Computer() {
  }
}

由于是瞎编的例子所以并没有列出其他配置,貌似只有cpu ,显卡, 显示器。都是被@Inject 标签贴上了。

tip1:
@Inject 表示这些成员变量需要注入,说白了就是需要DI框架帮我实例化
@Inject注意事项(来自官方,有些还是需要写demo 验证是啥意思)
在dagger 中并不是在哪都生效。
接口不能被构建
第三方类不能被注解
可配置的对象必须被配置。

这些贴着@Inject标签的配件儿,是由谁来提供的呢? 就Cput 而言,到底是AmdCpu 还是山寨的Cpu ? 接下来看下电脑组件工厂ComputerFactory 类。

@Module
public class ComputerFactory {

  @Provides
  public static Cpu provideCpu(AmdCpu cpu) {
    return cpu;
  }

  @Provides
  public static GraphicCard provideGraphicCard() {
    return new GraphicCard();
  }

  @Provides
  public static Monitor generateMonitor() {
    return new Monitor();
  }
}

这家被@Module标签贴上的工厂ComputerFactory提供的Cpu是Amd的,它还提供显卡和显示器,生产这些组件的方法就被@Provides 注释了。

tip2:
@Provides 注释一个方法用于声明如何提供被@Inject注释的对象。
@Provides 注解必须在被@Moudle 注释的类中
Computer 对象中被@Inject 注释的有Cpu , GraphicCard, Monitor 而ComputerFactory中被@Provides 注释的方法,负责生产它们。
注:
1.如果@provider注释的方法有参数那么必须要保证这个对象被@Inject注释
2.参数类型不能和返回类型相同,否则会报错:Found a dependency cycle

知道被贴上@Inject标签是谁来生产后,看下谁来组装Computer这个对象,看下ComputerShop:

@Component(modules =ComputerFactory.class)
public interface ComputerShop {
  public Computer getComputer();
}

这家ComputerShop 看起来提供由ComputerFactory 生产组件组装的Computer ,牌匾上写着
供应商ComputerFactory(modules =ComputerFactory.class) 到此我作为电脑的买家扮演Client的角色,而ComputerShop扮演Service的角色, 它负责卖给我电脑,而我没有办法干涉这家店给我组装什么样的电脑(因为是品牌机嘛)。

tip3:
@Inject 和@Provides注释的对象形成了一个对象图,这个对象图说明了这个类的实例以及成员变量都用哪些实例,通过Component获取这个对象图所描述的类, 这个Component 是个接口由@Component 注释,接口里的方法的返回值就是对象图的实例,而这个对象的创建所需要的成员变量需要用@Module注释的类来提供,Module中的方法由@Provides 注释。

简单来将上面的代码就是经历了这个过程:

  public void createComputerManually() {
    ComputerShop computerShop = new ComputerShop() {
      @Override
      public Computer getComputer() {
        Computer computer = new Computer();
        computer.cpu = new AmdCpu();
        computer.graphicCard = new GraphicCard();
        computer.monitor = new Monitor();
        return computer;
      }
    };

    Computer computer = computerShop.getComputer();
    Assert.assertNotNull(computer);
    Assert.assertEquals((computer.cpu instanceof AmdCpu), true);
  }

用了Dagger 后的代码。

@Test
 public void createComputerTest() {
   ComputerShop shop = DaggerComputerShop.create();
   Computer computer = shop.getComputer();
   Assert.assertNotNull(computer);
   Assert.assertEquals((computer.cpu instanceof AmdCpu), true);
 }

这里DaggerComputerShop 是Dagger 帮我们生成的代码是@Component注释的接口实例,能看到用了Dagger后的代码可读性高, 而且有效的分离了服务的使用和创建服务代码的细节。

Dagger 生成的代码

Dagger 会帮我们生成代码, 比如被@Component 注释的接口,会生成Dagger为前缀的类,比如ComputerShop接口生成的实例是DaggerComputerShop。

  @Test
  public void createComputerTest() {
    ComputerShop shop = DaggerComputerShop.create();
    Computer computer = shop.getComputer();
    Assert.assertNotNull(computer);
  }

看下DaggerComputerShop 的代码:

public final class DaggerComputerShop implements ComputerShop {
  private DaggerComputerShop(Builder builder) {}

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

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

  private Cpu getCpu() {
    return ComputerFactory_ProvideCpuFactory.proxyProvideCpu(new AmdCpu());
  }

  @Override
  public Computer getComputer() {
    return injectComputer(Computer_Factory.newComputer());
  }

  private Computer injectComputer(Computer instance) {
    Computer_MembersInjector.injectCpu(instance, getCpu());
    Computer_MembersInjector.injectGraphicCard(
        instance, ComputerFactory_ProvideGraphicCardFactory.proxyProvideGraphicCard());
    Computer_MembersInjector.injectMonitor(
        instance, ComputerFactory_GenerateMonitorFactory.proxyGenerateMonitor());
    return instance;
  }

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

    public ComputerShop build() {
      return new DaggerComputerShop(this);
    }

    /**
     * @deprecated This module is declared, but an instance is not used in the component. This
     *     method is a no-op. For more, see https://google.github.io/dagger/unused-modules.
     */
    @Deprecated
    public Builder computerFactory(ComputerFactory computerFactory) {
      Preconditions.checkNotNull(computerFactory);
      return this;
    }
  }
}

在调用DaggerComputerShop.create()时,实际上是通过Builder模式创建DaggerComputerShop 实例,并将ComputerFactory 对象通过Builder 赋值给DaggerComputerShop , 其中ComputerFactory对象是@Component 注释中modules参数 ComputerFactory.class的实例,以上是实例化Component并且把Module赋值给Component的过程。

接下来通过DaggerComputerShop 来获取Computer 对象时会发生什么,首先这里有个生成类Computer_Factory 先看看代码:

public final class ComputerFactory_ProvideCpuFactory implements Factory<Cpu> {
  private final Provider<AmdCpu> cpuProvider;

  public ComputerFactory_ProvideCpuFactory(Provider<AmdCpu> cpuProvider) {
    this.cpuProvider = cpuProvider;
  }

  @Override
  public Cpu get() {
    return provideInstance(cpuProvider);
  }

  public static Cpu provideInstance(Provider<AmdCpu> cpuProvider) {
    return proxyProvideCpu(cpuProvider.get());
  }

  public static ComputerFactory_ProvideCpuFactory create(Provider<AmdCpu> cpuProvider) {
    return new ComputerFactory_ProvideCpuFactory(cpuProvider);
  }

  public static Cpu proxyProvideCpu(AmdCpu cpu) {
    return Preconditions.checkNotNull(
        ComputerFactory.provideCpu(cpu),
        "Cannot return null from a non-@Nullable @Provides method");
  }
}

proxyProvideCpu 接受了AmdCpu 对象并且通过ComputerFactory的providerCpu 返回了AmdCpu实例, ComputerFactory 中provideCpu 方法被@Provide注释,由此看出,这个类就是连接@Inject 和 @Provider 的代理类, 同理ComputerFactory 另一个方法provideGraphicCard() 也会生成对应的类 ComputerFactory_ProvideGraphicCardFactory。
命名格式为Module名称 + 下划线 + provider方法名称 + Factory

Computer 对象所需要成员变量生成了,需要给它赋值, DaggerComputerShop.injectComputer 方法里可以看到调用了生成类Computer_MembersInjector来为Computer的成员变量赋值,看下代码:

public final class Computer_MembersInjector implements MembersInjector<Computer> {
  private final Provider<Cpu> cpuProvider;

  private final Provider<GraphicCard> graphicCardProvider;

  private final Provider<Monitor> monitorProvider;

  public Computer_MembersInjector(
      Provider<Cpu> cpuProvider,
      Provider<GraphicCard> graphicCardProvider,
      Provider<Monitor> monitorProvider) {
    this.cpuProvider = cpuProvider;
    this.graphicCardProvider = graphicCardProvider;
    this.monitorProvider = monitorProvider;
  }

  public static MembersInjector<Computer> create(
      Provider<Cpu> cpuProvider,
      Provider<GraphicCard> graphicCardProvider,
      Provider<Monitor> monitorProvider) {
    return new Computer_MembersInjector(cpuProvider, graphicCardProvider, monitorProvider);
  }

  @Override
  public void injectMembers(Computer instance) {
    injectCpu(instance, cpuProvider.get());
    injectGraphicCard(instance, graphicCardProvider.get());
    injectMonitor(instance, monitorProvider.get());
  }

  public static void injectCpu(Computer instance, Object cpu) {
    instance.cpu = (Cpu) cpu;
  }

  public static void injectGraphicCard(Computer instance, Object graphicCard) {
    instance.graphicCard = (GraphicCard) graphicCard;
  }

  public static void injectMonitor(Computer instance, Monitor monitor) {
    instance.monitor = monitor;
  }
}

最底下三段代码,就是为Computer 的实例赋值字段。 到此一个Computer 对象生成完了。
一个被标记为@Inject的对象,dagger会生成一个该对象的赋值类,命名格式为对象名 + MembersInjector。 到此生成的代码都过完了。可以看出Dagger2 通过代码生成的代理类,来创建客户端所需要的对象,并且为这个类注入需要注入的对象实例。

到此基本使用就Over了。

相关文章

网友评论

      本文标题:Dagger2 基本使用

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