依赖注入(二)—— Dagger的ComponentBuilder与ComponentFactory

Component Builder

在前面咱们提到,假如Dagger想要运用或许支持注入由外界供给的目标时,需求经过手动创立Module,传入外界目标,然后再将module目标设置给Component。

@Module
class CommonModule(
    private val context: Context
) {
    @Provides
    fun context(): Context = context
    @Provides
    fun provideSharedPreferences(): SharedPreferences {
        return context.getSharedPreferences("SP_FILE", Context.MODE_PRIVATE)
    }
}
@Component(modules = [CommonModule::class])
interface AppComponent {
    fun inject(application: MyApplication)
}
class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        DaggerAppComponent.builder()
            .commonModule(CommonModule(this))
            .build()
            .inject(this)
        ...
    }
}

在上面的例子中,DaggerAppComponent.builder()用于获取DaggerAppComponent的制作者实例,用于对DaggerAppComponent的实例进行一些装备。

当没有特殊声明时,Dagger会主动为Component生成对应的Builder,并且Dagger会主动检索该Component依靠的Module和Component,在Builder中为每一个Module和Component生成setter

public static Builder builder() {
  return new Builder();
}
public static final class Builder {
  private CommonModule commonModule;
  private BaseComponent baseComponent;
  private Builder() { }
  public Builder commonModule(CommonModule commonModule) {
    this.commonModule = Preconditions.checkNotNull(commonModule);
    return this;
  }
  public Builder baseComponent(BaseComponent baseComponent) {
    this.baseComponent = Preconditions.checkNotNull(baseComponent);
    return this;
  }
  public MyComponent build() {
    Preconditions.checkBuilderRequirement(commonModule, AModule.class);
    Preconditions.checkBuilderRequirement(baseComponent, BComponent.class);
    return new MyComponentImpl(commonModule, baseComponent);
  }
}

咱们也能够对Component的Builder进行显式声明,以复用一些代码或许将依靠Module和Component的初始化逻辑收拢:

@Component(modules = [CommonModule::class])
interface AppComponent {
    fun inject(application: MyApplication)
    // Subcomponent需求运用@Subcomponent.Builder
    // 关于Subcomponent的更多介绍参见后文[Component之间依靠项的复用]。
    @Component.Builder
    abstract class Builder : BaseBuilder<AppComponent> {
        fun context(context: Context): Builder {
            return setCommonModule(CommonModule(context))
        }
        protected abstract fun setCommonModule(module: CommonModule): Builder
    }
}
DaggerAppComponent.builder()
    .context(this)
    .build()
    .inject(this)

需求留意的是,显式声明Builder时,咱们有必要为Component依靠没有默许结构函数的Module依靠界说setter,不然将编译失利;并且在运用Builder结构Component时,也有必要调用这些setter,不然将会抛出运行时过错。

Component Factory

除了运用Builder来定制Component外,Dagger还供给了Component Factory办法来定制Component。

那么已然已经有Builder了,为何还要供给Factory呢?

既生Builder,何生Factory?

这是由于Builder办法在运用上有一些不方便,当咱们要结构的Component有多个依靠的Module和Component时,Component依靠没有默许结构函数的Module依靠有必要要手动调用setter,然后才干调用build函数生成Component实例。而咱们在运用时很容易忘记某个setter的调用,这种过错又不会在编译期间被检测出来,仅在运行时抛出过错。

因而,Dagger供给了Factory,经过严格约束Factory中create办法的参数为Component依靠的Module和Component实例,将原本在运行时的查看提前到了编译时,从而提升了运用体验。

接下来让咱们看看怎么运用Component Factory。

怎么运用

和Builder不相同,默许情况下Dagger并不会为Component生成相应的Factory完成,咱们需求显式的为Component声明Factory:

@Component(
    modules = [CommonModule::class],
    dependencies = [BaseComponent::class]
)
interface AppComponent {
    fun inject(application: MyApplication)
    // Subcomponent需求运用@Subcomponent.Factory。
    @Component.Factory
    interface Factory {
        // Factory中只能界说一个办法,且该办法承受Component依靠的Module和Component,
        // 回来Component实例。
        fun create(
            baseComponent: BaseComponent, 
            commonModule: CommonModule
        ): MyComponent
    }
}

与Builder相同,咱们也有必要在create办法中将Component依靠没有默许结构函数的Module依靠作为参数传递,不然将会编译过错。

编译后Dagger就会主动为咱们生成当时Component对应的Factory完成类:

public static MyComponent.Factory factory() {
  return new Factory();
 }
private static final class Factory implements MyComponent.Factory {
  @Override
  public MyComponent create(BaseComponent baseComponent, CommonModule commonModule) {
    Preconditions.checkNotNull(baseComponent);
    Preconditions.checkNotNull(commonModule);
    return new MyComponentImpl(commonModule, baseComponent);
  }
}

运用时和Builder类似,经过factory静态办法获取工厂实例,调用create办法即可:

class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        DaggerAppComponent.factory()
            .create(baseComponent, CommonModule(this))
            .inject(this)
        ...
    }
}

将外部目标添加到Dagger中的简便办法

让咱们回到文章开端的例子中,为了向Dagger中供给Context,不管运用Builder仍是Factory,咱们都不得不为其创立一个CommonModule实例,然后设置给AppComponent,这个步骤多少仍是有些繁琐,因而Dagger为咱们供给了一个新的注解@BindsInstance用于直接将外部目标绑定到Dagger上。

@BindsInstance有两种运用办法:

在Builder中运用

@Component(modules = [PrefModule::class])
interface AppComponent {
    fun inject(application: MyApplication)
    @Component.Builder
    interface Builder {
        @BindsInstance
        fun context(context: Context): Builder
        // 有默许结构函数的Module能够省略setter界说
        // fun prefModule(module: PrefModule): Builder
        fun build(): MyComponent
    }
}
// module中不再需求context的Provides-method
@Module
class PrefModule {
    @Provides
    fun provideSharedPreferences(
        context: Context // context现在能够作为依靠项直接注入了
    ): SharedPreferences {
        return context.getSharedPreferences("SP_FILE", Context.MODE_PRIVATE)
    }
}
class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        DaggerAppComponent.builder()
            .context(this)
            .build()
            .inject(this)
        ...
    }
}

在Factory中运用

@Component(modules = [PrefModule::class])
interface AppComponent {
    fun inject(application: MyApplication)
    @Component.Factory
    interface Factory {
        fun create(
            @BindsInstance context: Context
            // 相同的有默许结构函数的Module能够省略
            // , prefModule: PrefModule
        ): MyComponent
    }
}
// module中不再需求context的Provides-method
@Module
class PrefModule {
    @Provides
    fun provideSharedPreferences(
        context: Context // context现在能够作为依靠项直接注入了
    ): SharedPreferences {
        return context.getSharedPreferences("SP_FILE", Context.MODE_PRIVATE)
    }
}
class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        DaggerAppComponent.factory()
            .create(this)
            .inject(this)
        ...
    }
}