匕首:访问在子组件中创建的两个级别的项目

Dagger: Accessing item created two levels deep in a subcomponent

我有一个子组件需要从中提取一些东西:

@Subcomponent(modules = {SubModule.class})
@SubScope
public interface SubComp {
  // ...
  Thing getThing();
}

每次调用 #getThing 时,我都需要一个 Thing 的新实例。

Thing 也有自己的项目轨道,需要用它来创建。自然地,我的直觉是为它创建另一个子组件:

@Subcomponent(modules = {ModuleThing.class})
@ThingScope
public interface SubCompThing {
  // ...
}

但这是我的难题:实际上应该创建哪段代码 Thing

如果我将提供程序放入 SubModule,然后我需要将该实例绑定到 SubCompThing,但我收到有关绑定多个实例的警告。该警告在我的构建中是致命的,实际上声明该警告将来会成为错误:

@Module(subcomponents = {SubCompThing.class})
interface SubModule {
   @Provides
   static providesThing(SubCompThing.Factory thingCompFactory) {
     Thing thing = new Thing();
     thingComp = thingCompFactory.create(thing);  // Warning about duplicate binding.
     // Do some stuff with thingComp
     return thing;
   }
}

如果我 SubCompThing 自己直接创建 Thing,我的警告会变成一个错误,问题相同:

@Module
interface ModuleThing {
    @Provides
    @ThingScope
    static Thing providesThing() {
      return new Thing();
    }
}

@Module(subcomponents = {SubCompThing.class})
interface SubModule {
   @Provides
   static Thing providesThing(SubCompThing.Factory thingCompFactory) {
     thingComp = thingCompFactory.create();
     // Do some stuff with thingComp
     return thingComp.getThing();
   }
}

(Dagger 编译 Thing 绑定了两次,因为它有两个提供程序。)

我怎样才能让我的顶级 SubComp return 新的 Thing 随需应变,并且让每个 Thing 都有自己的子组件实例与他们?

您将需要使用 qualifier annotation

这里问题的根源在于子组件从它们的父组件继承绑定,因此在您最深的子组件 SubCompThing 中,您组件上暴露的 Thing 绑定很可能会注入您在 SubComp 的 SubModule 中安装的 Thing 绑定...因为 Dagger 不知道您在 SubModule 中的 @Provides 方法本身将调用 SubCompThing 的 getThing() 方法!

正如我们在评论中讨论的那样,您所描述的内容非常类似于 Dagger 关于 subcomponents for encapsulation 的官方文档,它偷偷地描述但没有描述它对限定符注释的依赖:

@Subcomponent(modules = DatabaseImplModule.class)
interface DatabaseComponent {
  @PrivateToDatabase Database database();
  /* ^^^^^^^^^^^^ */
}

这是消除歧义的技巧:您的 ModuleThing 应该绑定 @Provides static @PrivateThing Thing,而您的 SubCompThing 应该公开 @PrivateThing Thing getThing(),这样唯一绑定的不合格事物就绑定在您的 SubModule 中。 (顺便说一句,这将允许您在 SubCompThing 中注入一个事物,委托 SubModule 的实现从 ModuleThing 的调用堆栈中创建一个全新的 SubCompThing 实例。)

我在这里定义了一个便宜的限定符注释,但欢迎您使用 @Named 来制作它的原型。

@Qualifier @Retention(RetentionPolicy.RUNTIME) public @interface PrivateThing {}

@Module
interface ModuleThing {
  @Provides
  @ThingScope
  @PrivateThing
  static Thing providesThing() {
    return new Thing();
  }
}

@Subcomponent(modules = {ModuleThing.class})
@ThingScope
public interface SubCompThing {
  @PrivateThing getThing();
}

到那时,您的 SubModule 将无需额外修改即可工作,将 Thing 的创建抽象为 SubCompThing 的黑盒实现(我更喜欢这种方式,这样您就不必带来 Thing 的实例化细节进入子模块)。这也意味着您可以保持 Thing 无作用域,这样您就可以实例化多个对象:当您调用提供者时,您会看到一些新的东西。 That way you can make both thing1 and thing2.