在使用Dagger2,我们都遇到了一种情况,希望从外部将依赖项传递给我们的模块,以便于在其他类中注入,最常见的例子是将应用程序Context传递给AppModule。还有其他用例,例如将用户ID传递给用户详细信息屏幕,我们需要用户模块从外部获取用户ID。
以前有两种方法可以解决这个问题,但Dagger 2.22引入了另一种方式。尝试分析应用最佳解决方案的如下三种方法。
方法一:通过构造函数参数传递给模块
@Module
class AppModule(private val context: Context) {
@Provides
fun providesAppContext() = context
}
@Component(modules = [AppModule::class])
interface AppComponent {
// ....
}
在创建组件时,只需将Context传递给模块。
class App : Application() {
override fun onCreate() {
super.onCreate()
DaggerAppComponent.builder().appModule(AppModule(this)).build()
}
}
生成类 DaggerAppComponent 实现 AppComponent 接口
public final class DaggerAppComponent implements AppComponent {
private DaggerAppComponent() {}
public static Builder builder() {
return new Builder();
}
public static AppComponent create() {
return new Builder().build();
}
public static final class Builder {
private Builder() {}
/**
* @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 appModule(AppModule appModule) {
Preconditions.checkNotNull(appModule);
return this;
}
public AppComponent build() {
return new DaggerAppComponent();
}
}
}
缺点:这看起来很直接,但不能使我们的模块抽象(Dagger2建议模块中不应该有状态,且模块方法应该为静态方法,这样可以提升性能)并且因为我们有时需将模块实例传递给我们的组件。
方法二:使用 @Component.Builder 和 @BindsInstances
在Dagger2.12中,我们得到了两个新注释@Component.Builder和@BindsInstances,用于执行我们之前通过将参数传递给构造函数所做的事情。
@Component(modules = [AppModule::class])
interface AppComponent {
fun inject(activity: MainActivity)
@Component.Builder
interface Builder {
@BindsInstance
fun context(context: Context): Builder
fun build(): AppComponent
}
}
@Module
class AppModule {
// ...
}
顾名思义,@BindsInstances将实例绑定到组件。通过这样做,我们将Context添加到我们的Dagger中,现在我们可以像以前一样获得Context。
您的Builder接口书写应遵循一些规则。
- Builder必须至少有一个返回Component类型的方法,在我们的例子中它是build()函数。
- 用于绑定实例的构建方法以外的方法应返回Builder类型。
- 用于绑定依赖项的方法不能包含多个参数,如果要绑定更多依赖项,则为每个依赖项创建不同的方法。
这就是我们的组件创建方式。
class App : Application() {
override fun onCreate() {
super.onCreate()
DaggerAppComponent.builder().application(this).build()
}
}
要了解@Component.Builder的工作原理,我们必须查看生成的代码。我们的组件接口名称是AppComponent,因此dagger生成了一个名为DaggerAppComponent的类,它提供了我们组件的实现。Dagger使用Builder模式创建组件,因此在我们的DaggerAppComponent类中,我们有一个名为Builder的静态最终类。现在因为我们在AppComponent中提供了构建器接口,DaggerAppComponent中存在的内部Builder类实现了我们创建的Builder接口。
public final class DaggerAppComponent implements AppComponent {
private DaggerAppComponent(Context application) {}
public static AppComponent.Builder builder() {
return new Builder();
}
@Override
public void inject(MainActivity activity) {}
private static final class Builder implements AppComponent.Builder {
private Context application;
@Override
public Builder application(Context context) {
this.application = Preconditions.checkNotNull(context);
return this;
}
@Override
public AppComponent build() {
Preconditions.checkBuilderRequirement(application, Context.class);
return new DaggerAppComponent(application);
}
}
}
这个解决方案比前一个更好
我们不必再传递构造函数参数,因此我们的模块可以是无状态的,并且可以包含所有静态方法。
问题
没有与性能相关的问题了,但是如果我们想要将许多实例绑定到组件,那么它将创建一个长链,如果我们忘记调用链中的任何方法,它将导致运行时异常。这就是要解决的问题。
DaggerAppComponent
.builder()
.application(this)
.name("Android")
.size(1)
.build()
方法三:使用@Component.Factory
使用dagger 2.22版本,我们得到了另一个名为@Component.Factory的注释,以解决因使用@Component.Build而引入的问题。
现在,使用Dagger 2.22,我们可以在每个参数中使用@BindsInstances。
@Component(modules = [AppModule::class])
interface AppComponent {
fun inject(activity: BaseActivity)
@Component.Factory
interface Factory {
fun create(@BindsInstance context: Context): AppComponent
}
}
与@Component.Builder一样,您的Factory接口应遵循一些规则。
- 你的工厂不能有超过1种方法。如果你想绑定许多依赖项,那么不是为每个依赖项创建方法(就像我们对@ Component.Builder所做的那样),你只需为每个依赖项添加一个新参数。
- 您的方法必须返回Component的类型或Component的父类型。
现在我们的组件将像这样创建。
class App : Application() {
override fun onCreate() {
super.onCreate()
DaggerAppComponent
.factory()
.create(this)
}
}
要了解@Component.Factory如何工作,我们必须查看生成的代码。现在,在DaggerAppComponent而不是Builder类中,我们得到Factory类,它正在实现我们创建的Factory接口。
public final class DaggerAppComponent implements AppComponent {
private DaggerAppComponent(Context context) {}
public static AppComponent.Factory factory() {
return new Factory();
}
@Override
public void inject(BaseActivity activity) {}
private static final class Factory implements AppComponent.Factory {
@Override
public AppComponent create(Context context) {
Preconditions.checkNotNull(context);
return new DaggerAppComponent(context);
}
}
}
这个解决方案比前一个更好?
- 由于我们没有为每个依赖项添加方法,因此在此情况下都不会有长链和多个方法。
- 每个依赖项都会在函数中添加一个参数,如果我们忘记传递任何依赖项,我们将得到编译时错误而不是运行时异常
与@Component.Builder和@Component.Factory一样,这些注释@SubComponent.Builder和@SubComponent.Factory也有子组件版本。