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

Posted

技术标签:

【中文标题】匕首:访问项目在子组件中创建了两个级别【英文标题】:Dagger: Accessing item created two levels deep in a subcomponent 【发布时间】:2022-01-09 11:38:34 【问题描述】:

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

@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按需返回新的Things,并让每个Things 都有自己的子组件实例与之关联?

【问题讨论】:

您所描述的内容与 Dagger 在subcomponents for encapsulation 上的官方文档非常接近。你的名字让人有点难以理解;我是否正确理解根组件(不存在)包括引用 SubComp(@SubScope,显示)的模块(不存在),然后 SubComp 安装 SubModule(显示),它引用 SubCompThing(@ThingScope,显示),它安装 ModuleThing ,它实际上实例化了一个 @ThingScope 事物,对吧?使用 @SubScope 注释 SubModule.providesThing 是否有帮助? 是的,您已经掌握了要点。 SubComp 位于某个父组件内部。 SubModule#providesThing 没有 用范围注释,因为我需要多个 Things 超出 SubComp 【参考方案1】:

您将需要使用qualifier annotation。

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

正如我们在 cmets 中所讨论的,您所描述的内容非常类似于 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 的实例化细节带入 SubModule )。这也意味着您可以保持 Thing 无范围,以便您可以实例化多个它们:当您调用提供者时,您会看到一些新的东西。 That way you can make both thing1 and thing2.

【讨论】:

以上是关于匕首:访问项目在子组件中创建了两个级别的主要内容,如果未能解决你的问题,请参考以下文章

django makemigration 在子应用程序上抛出错误

我如何访问 iOS 项目的 NSUserDefault 以做出原生反应?

在子组件中创建新的子组件

为啥我的 java 编译器级别与我安装的项目方面不匹配?

如何在招摇中访问图形api?

text 在子文件夹中创建组件