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)
...
}
}