Dagger2使用简析(一)——简单注入

NOTE1 : Dagger2的官网地址: https://google.github.io/dagger/

NOTE2 : 使用前请先了解JAVA中的注解基本知识,如果对实现原理感兴趣可以了解下APT技术(不了解也不影响正常使用)

NOTE3: 本文所涉及的示例已上传至Github:
https://github.com/bailandi/Dagger2Demo


在具体使用前,首先来了解一个概念——依赖注入
依赖注入是由于类之间的依赖关系产生的,比如:

//产品依赖于操作工
public class Product{
  private Worker mWorker;
}

这个时候想要产生mWorker的实例,通常有两种方法:

//直接new
mWorker = new Worker();

//通过参数传入
public  Product(Worker worker){
  mWokrer = worker;
}

以上就是典型的依赖注入,那么为什么要使用Dagger2来进行依赖注入呢?
在回答这个问题前,我们首先分析以上两种产生mWorker实例的方式存在什么问题。

  • 对于第一种方式,假设当业务扩充,此时Worker的构造需要依赖于工具小刀KnifeWorker的构造函数发生变化,我们不得不对Product做出修改,这严重违反了开闭原则
  • 对于第二种方式,假设工厂Factory依赖于Product,此时Product的位置就会与第一种的Worker一样。我们可以从迪米特原则(最少知识原则)——一个类应该对自己直接耦合(依赖)的类知道最少中找到这一问题产生的根本原因,显然,Factory并不关心Worker,然而Product却在构造时将对Wokrer的依赖带入到了Factory
    Dagger2使用简析(一)——简单注入_第1张图片
    依赖关系.png

为了解决这种依赖关系对业务扩展时造成的糟糕体验,我们使用Dagger2来进行依赖注入


一切实践从问题出发,首先我们来解决第一个问题,

1. 如何使用Dagger2注入一个简单对象到目标类中

让我们先复制一个示例,它包含下面三部分

@Module
public class MainModule {
    @Provides
    public Product provideProduct() {
        return new Product();
    }
}

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

public class MainActivity extends AppCompatActivity {
    @Inject
    Product mProduct;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        //...省略无关代码

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

        mProduct.create();
    }
}

我们依次对三个部分所涉及的内容进行解释:

1.1 @Module

@Module注解用于获取对象实例的类,Dagger2根据该注解知道应该去哪个类里获取对象实例

1.2 @Provides

@Provides注解用于module类中获取对象实例的方法,Dagger2根据该注解及方法的返回值类型将对象实例注入到对应的引用中

1.3 @Inject

@Inject用于注解构造函数或成员变量

  • 当作用于成员变量时,Dagger2根据该注解及成员变量的类型,从moudle中得到相应实例。
    注意:成员变量的访问修饰符不能是private

  • 当作用于构造函数时,就是Dagger2对于获取对象实例的第二种方式,比如上面的例子其实可以直接在Product的构造函数上注解@Inject,并移除@Provides注解的方法

     @Inject
     public Product() {
     }
    

    但是@Inject并非适用于所有的地方,比如以下三种情况:

    • 不具有构造函数的接口
    • 远程引入的三方库中的类无法被添加注解
    • 通过建造者模式等方式可配置化的进行构造的对象

    而使用@Provides可以更好的处理这些问题

1.4 @Component

@Component注解用于担任连接桥梁的接口,其两端分别是在@Component的参数中指定的modules数组在方法参数中指定的具体类
注意:方法参数必须是要注入的具体类,而非其父类或接口

1.5 DaggerMainComponent

在创建Module和Component后,我们需要build ——> Make Project,这样APT会在指定目录下生成对应的java文件,其中就会包含名如DaggerXX的的文件,它实现了我们所创建的Component接口,最终要完成注入,只需要像这样调用:

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

2. 简单分析自动生成的代码

2.1 DaggerXX类

public final class DaggerMainComponent implements MainComponent {
  private final MainModule mainModule;

  private DaggerMainComponent(MainModule mainModuleParam) {
    this.mainModule = mainModuleParam;
  }

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

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

  @Override
  public void inject(MainActivity activity) {
    injectMainActivity(activity);
  }

  private MainActivity injectMainActivity(MainActivity instance) {
    MainActivity_MembersInjector.injectMProduct(
        instance, MainModule_ProvideProductFactory.provideProduct(mainModule));
    return instance;
  }

  public static final class Builder {
    private MainModule mainModule;

    private Builder() {}

    public Builder mainModule(MainModule mainModule) {
      this.mainModule = Preconditions.checkNotNull(mainModule);
      return this;
    }

    public mainComponent build() {
      if (mainModule == null) {
        this.mainModule = new mainModule();
      }
      return new DaggerMainComponent(mainModule);
    }
  }
}

它实现了我们所创建的Component接口,内部使用了建造者模式,方便根据不同需求构建该对象实例,比如我们可以传入module实例替换默认的无参实例,关键看一下我们定义的inject方法的具体实现。

2.2 MainActivity_MembersInjector类和MainModule_ProvideProductFactory类

public final class MainActivity_MembersInjector implements MembersInjector {
  //...省略无关代码

  public static void injectMProduct(MainActivity instance, Product mProduct) {
    instance.mProduct = mProduct;
  }
}
public final class MainModule_ProvideProductFactory implements Factory {
   //...省略无关代码

  public static Product provideProduct(MainModule instance) {
    return Preconditions.checkNotNull(
        instance.provideProduct(), "Cannot return null from a non-@Nullable @Provides method");
  }
}

其实关键的方法就是injectMProduct()provideProduct(),一看就懂,没什么可细说的。

3. 含有参数(依赖关系)的构造

Product必须与Worker关联时,有如下构造函数

 public Product(Worker worker) {
     mWorker = worker;
 }

此时我们提供Product实例对象时需要作出以下修改

@Provides
public Product provideProduct(Worker worker) {
    return new Product(worker);
}

即我们需要传入Worker来构建Product,那么Worker实例如何提供呢?其实同样的,我们可以使用简单构造时的两种方式提供Worker实例,具体就不在赘述,可以自行实践。

下一篇
,我们将对问题进行升级,并了解@Scope、@Qualifier、@binds、dependencies、Lazy的使用

你可能感兴趣的:(Dagger2使用简析(一)——简单注入)