Dagger2 将实例绑定到组件的新方法(@Component.Factory and @SubComponent.Factory)

 在使用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也有子组件版本。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值