【问题标题】:Dagger 2 Adding a subcomponent to a parent componentDagger 2 将子组件添加到父组件
【发布时间】:2018-06-29 11:20:25
【问题描述】:

社区您好,我无法理解 dagger 2 以新方式添加子组件(在 dagger 2.7 中添加)。请参见下面的示例:

@Component(modules = {AppModule.class, MainActivityBinder.class})
@Singleton
interface AppComponent
{
   inject(MyApplication _)
}

@Subcomponent(modules = ActivityModule.class)
interface ActivitySubcomponent
{
   inject(MainActivity _)

   @Subcomponent.Builder
   interface Builder
   {
      @BindInstance
      Builder activity(Activity activity)

      ActivitySubcomponent build();
   }
}

初始步骤:我有AppComponent 是我的根组件,它为AppModule 提供单例(改造、okhttp 等)。在ActivitySubcomponent 中,我提供ActivityModule 具有指定给该活动的依赖项。现在必须将子组件添加到AppComponent,因此我以新的方式创建了名为MainActivityBinder 的指定模块,该模块具有@Module.subcomponents 注释,指向绑定子组件,但我有第一个问题,它的主体应该是什么绑定模块?

@Module(subcomponents = ActivitySubcomponent.class)
public class MainActivityBinder
{
  //what body of this class should be ??
}

我知道,这个想法是我可以绑定子组件或其构建器。第二个问题何时绑定构建器,何时绑定子组件?例如我的ActivitySubcomponent 需要活动上下文,所以我创建了为ActivityModule 提供上下文的构建器,在这种情况下,在MainActivityBinder 中提供一个构建器会更好吗?第三个问题如何调用组件构建器以及如何获取应用组件的子组件?在标准子组件工厂中,我添加了返回子组件的AppComponent 方法,我可以定义参数(例如给出活动上下文,如下所列)

@Component(modules = {AppModule.class})
@Singleton
interface AppComponent
{
   ActivitySubcomponents newActivitySubcomponents(Activity activity);

   inject(MyApplication _);
}

// in MainActivity
appComponent.newActivitySubcomponents(this).build().inject(this);

所以在新的子组件添加方法中实现了这种行为?

【问题讨论】:

  • 为什么不使用 dagger-android ?一切都更加简单。
  • @Zuluft 你的建议离题了,我的目标是理解 Module.subcomponents,我读到的文档说:“使用Module.subcomponents更好,因为它允许Dagger检测是否曾经请求过子组件。通过父组件上的方法安装子组件是对该组件的显式请求,即使从未调用过该方法。”,所以我想尝试测试新的工作方式,但我不能正确定义的绑定模块。

标签: android dagger-2 subcomponent


【解决方案1】:
  1. 您的模块 MainActivityBinder 可以为空,如果您没有其他任何东西可以绑定它,则应该为空。当您只使用Module.includes 时,空(仅注释)模块也很有用,例如当您想将模块列表保留在一个地方而不是在多个组件中复制它时。注释上的subcomponents 属性足以让Dagger 了解您要做什么。

  2. 当且仅当它没有 @BindsInstance 方法或可实例化模块(Dagger 无法实例化)时,您才能注入 FooSubcomponent 或 Provider。如果您的所有模块都是接口、抽象类或具有公共零参数构造函数的模块,那么您可以直接注入子组件。否则你应该注入你的子组件构建器。

  3. 您可以通过创建一个在 AppComponent 上返回它的方法来访问您的子组件构建器,就像对图中存在的任何绑定一样:

    @Component(modules = {AppModule.class, MainActivityBinder.class})
    @Singleton
    interface AppComponent {
      ActivitySubcomponent.Builder activitySubcomponentBuilder();
    
      inject(MyApplication _)
    }
    

    您也可以将其注入您选择的对象中。

    @Inject ActivitySubcomponent.Builder activitySubComponentBuilder;
    activitySubComponentBuilder.activity(this).build().inject(this);
    
    // You can also inject a Provider<ActivitySubcomponent.Builder> if you want,
    // which is a good idea if you are injecting this directly into your Application.
    // Your Application will outlive your Activity, and may need to inject several
    // instances of the Activity across application lifetime.
    @Inject Provider<ActivitySubcomponent.Builder> activitySubComponentBuilderProvider;
    activitySubComponentBuilderProvider.get().activity(this).build().inject(this);
    

虽然当您可以轻松调用组件上的方法(返回 Builder 或返回子组件)时,注入子组件构建器似乎没有太多优势,但注入子组件构建器有几个优点建设者:

  • Dagger 无法判断您是否调用了组件上的方法,因此即使您的子组件未使用,它也会生成并编译代码。 Dagger 可以告诉你是否曾经尝试注入构建器,所以如果没有子组件/构建器注入并且没有方法,Dagger 将跳过为子组件生成代码。
  • 如果您的代码库足够大,以至于您必须将其拆分为不同的目标进行编译,工厂方法技术可能会在某些依赖周期中捕获您,在这些周期中,您的应用程序的组件和模块依赖于所有内容,您只能访问您的组件本身的子组件。使用可注入的子组件构建器,您可以有更多选择来访问您的子组件或构建器。

【讨论】:

  • 感谢@Jeff Bowman 的深入解释。根据官方documentation 的第二个选项,我还有一个关于交换子组件进行测试的问题。如何为设置测试子组件MainActivitySubcomponentTest(从 MainActivitySubcomponent 扩展)配置AppComponent,我尝试在 Component.Builder 中执行 setter,但不起作用。
  • @Heroes84:不客气!不幸的是,在不创建不同的 AppComponentForTest 的情况下,没有真正的替代子组件的好方法,因为子组件实现是生成的组件代码的私有细节。如果您希望能够替换您的父组件,那么您可以将您的活动子组件变成一个完整的 @Component 并具有依赖关系——但这需要大量单独的工作和权衡。如果你的注入结构很好(直接注入子组件),你也可以手写或模拟一个子组件及其构建器,并在测试中使用它们。
  • 如果我使用 AppComponent 将构建器注入到 Activity 成员变量中,那么之后我不能使用 ActivityComponent 对其他字段执行相同的操作,对吧?换句话说,我不能在具有 2 个不同组件的同一类上使用字段注入。
  • 另外,考虑到 Proguard 应该删除未使用的部分,不必要生成的代码甚至是一个问题吗?
  • @FlorianWalther 1. 两次注入同一个对象(包括两个不同的组件)充其量是容易发生事故的,如果它可以工作的话; Dagger 组件不喜欢注入它们无法完全注入的类。也就是说,子组件可以访问其所有父组件的绑定,因此注入 Activity 组件一次就可以了。 2. Proguard 可能为您修剪未使用的代码,但您仍然需要满足您未使用的方法所需的所有绑定,而且您的 Proguard 时间会随着您的输入和 Proguard 的增加而增加可能在删除该代码方面没有那么有效。
猜你喜欢
  • 2019-09-08
  • 2015-06-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-12-01
  • 2016-07-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多