Dagger2 Component自定义构造以及@BindsInstance使用

229 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 9 天,点击查看活动详情

image.png

首先我们了解一下如何写Component的构造函数,我们之前的文章里面都没有关于component的构造,但是我们看看dagger给我们实现的里面:

public static Builder builder() {
  return new Builder();
}
​
public static ExampleComponent create() {
  return new Builder().build();
}

Builder类:

public static final class Builder {
  private ExampleModule exampleModule;
​
  private Builder() {
  }
​
  public Builder exampleModule(ExampleModule exampleModule) {
    this.exampleModule = Preconditions.checkNotNull(exampleModule);
    return this;
  }
​
  public ExampleComponent build() {
    if (exampleModule == null) {
      this.exampleModule = new ExampleModule();
    }
    return new ExampleComponentImpl(exampleModule);
  }
}

这里需要注意一下Component是通过build构造的。既然实现里面有这个,那我们是不是可以在Component里面去自定义自己的Build和Create呢?在创建的时候做一些我们自己的事情?首先我们写一个自己的Build类:

@Component.Builder
interface Builder {
    fun build(): ExampleComponent
}

需要注意的是定义的这个Interface必须要加上@Component.Builder,要不然Dagger2是不认识的。再看看生成的代码:

private static final class Builder implements ExampleComponent.Builder {
  @Override
  public ExampleComponent build() {
    return new ExampleComponentImpl(new ExampleModule());
  }
}

这里就会有相应的一个实现。

下面再看看Create方法如何实现:

@Component.Factory
interface Factory{
    fun  create():ExampleComponent
}

这里就需要加上@Component.Factory关键字,并且build类也不能再要了。再看看实现:

private static final class Factory implements ExampleComponent.Factory {
  @Override
  public ExampleComponent create() {
    return new ExampleComponentImpl(new ExampleModule());
  }
}

这样就实现component的自定义构造啦,builder方法以及factory方法都可以。问题我们为什么要自定义构造呢?其实我们是想做一些事情,我们是想在Component实例化的时候传一些参数进去,这个时候我们就需要使用到@BindsInstance关键字啦!

比如我们想在构造里面绑定一个context,context是我们几乎每个模块都需要使用的,如果我给当前的Component绑定了,那么其他地方就可以随意使用了。我们先看看怎么给builder里面绑定:

@Component.Builder
 interface Builder {
    @BindsInstance
     fun setContext(context: Context):Builder
     fun build(): ExampleComponent
 }

其实就是在一个方法前面加@BindsInstance就可以了,如果不加@BindsInstance编译的时候就会报错的哦。再看看生成的类:

private static final class Builder implements ExampleComponent.Builder {
    private Context setContext;
​
    @Override
    public Builder setContext(Context context) {
      this.setContext = Preconditions.checkNotNull(context);
      return this;
    }
​
    @Override
    public ExampleComponent build() {
      Preconditions.checkBuilderRequirement(setContext, Context.class);
      return new ExampleComponentImpl(new ExampleModule(), setContext);
    }
  }

竟然把setContext作为了参数!那命名上还是注意一点吧。还有一点就是setContext一定要返回Builder,要不然在调用了这个方法之后就没有办法链式调用了,那在注入的时候也需要传入context这个参数了:

DaggerExampleComponent.builder().setContext(this.applicationContext).build().inject(this)

那既然传入了我该如何使用这个context呢?

其实很简单在其他注入对象要使用context的地方,这个context就会被用起来,例如我们上次定义的Woman这个类加入context:

@ExampleScope
@Provides
@Named("Woman")
fun providerWoman(context: Context):Person{
    return Woman()
}

在dagger2生成的代码里面就变成这个样子了:

private void initialize(final ExampleModule exampleModuleParam, final Context setContextParam) {
  this.setContextProvider = InstanceFactory.create(setContextParam);
  this.providerWomanProvider = DoubleCheck.provider(ExampleModule_ProviderWomanFactory.create(exampleModuleParam, setContextProvider));
}

它就会把context传入了。