【问题标题】:Implementing a Singleton Builder in Dagger在 Dagger 中实现单例生成器
【发布时间】:2019-11-20 22:54:01
【问题描述】:

我有一个类,我想通过 Dagger 注入。它需要一个构建器,因为它的一些参数仅在运行时才知道。我也希望这个类是单身人士。

也就是说,我们的代码库有几部分需要这个类的实例,但实际上只有一部分代码知道如何设置它。

设置发生在应用程序的早期 - 在任何人实际尝试使用单例之前 - 但在 Dagger 已经初始化其根组件之后。

代码的其他部分访问对象的正确方法是什么?

class S {
  private S(Foobar foobar) {
    // ...
  }

  @Singleton
  public static class Builder {
    Foobar foobar;
    @Inject
    public Builder() {}

    public Builder setFoobar(Foobar foobar) {
      this.foobar = foobar;
    }

    public S build() {
      return new S(foobar);
    }
  }
}

class Main {
  private Foobar foobar = new Foobar();
  private final S s;

  @Inject
  public Main(S.Builder sBuilder) {
    s = sBuilder.setFoobar(foobar).build();
  }
}

class Other {
  private final S s;

  @Inject
  public Other(/* What do I put here to get access to S? */) {
  }

编辑: 为清楚起见,让我声明此示例中的 Foobar 是在应用程序的早期创建的,但在 Dagger 和应用程序的高级结构已被实例化之后。这个特定的程序是一个 Android 应用程序;在这种情况下,Foobar 是一个膨胀的视图,S 是该视图的控制器。

只有一个Foobar 和一个S。我们代码的各个部分都希望能够与S 通信,但实际上只有一部分代码可以创建它。

【问题讨论】:

  • 这是一个奇怪的设计。使用构建器到组合根中的实例,然后将实例/单例传递给容器以供使用。
  • @Nkosi 我不确定我是否遵循。直到设置注入后的某个时间,我才能创建 S。 S 需要一个在应用程序启动后期发生的对象。

标签: java dependency-injection dagger-2


【解决方案1】:

您可以使用BindsInstance 来表示https://dagger.dev/api/2.10/dagger/BindsInstance.html

您的应用程序创建RootComponent。在创建期间,它应该提供Foobar 的实例。我们来看看伪代码

class S {
    private S(Foobar foobar) {
        // ...
    }

    public static class Builder {
        Foobar foobar;

        public Builder() {}

        public Builder setFoobar(Foobar foobar) {
            this.foobar = foobar;
        }

        public S build() {
            return new S(foobar);
        }
    }
}

@Module
public static class RootModule {

    @Provides
    @Singleton
    public static S provideS(Foobar foobar) {
        return new S.Builder().setFoobar(foobar).build();
    }
}

@Singleton
@Component(module = {RootModule.class})
public interface RootComponent {
    @Component.Factory
    interface Factory {
        public RootComponent create(@BindsInstance Foobar foobar)
    }
}

public class Application {
    private RootComponent createComponent() {
        return DaggerRootComponent.factory().create(new Foobar())
    }
}

更新

此特定程序是 Android 应用程序;在这种情况下,Foobar 是一个膨胀的 View,而 S 是该 View 的控制器。

我强烈建议您不要保留对 View 的引用。这可能会产生细微的错误和内存泄漏。相反,我建议您引入某种事件总线,它将成为 Dagger 图中的单例,并在 S(视图控制器)和消费者之间共享。这个偶数总线将用于消费者和S(视图控制器)之间的通信。

【讨论】:

  • 问题是在创建根组件时 foobar 不可用。
  • @davemankoff 您能否详细说明 Application / Foobar / Dagger 组件的生命周期。请注意,RootComponent 在我的示例中只是一个随机名称。你可以有一个RootWithFoobarComponent,它是AnotherRootComponent 的子组件,或者依赖于AnotherRootComponent。我的示例的目的是展示如何将运行时依赖项放入 Dagger 图中。
  • 我在问题的底部添加了更多信息。我们的应用程序中没有任何子组件,所以我不确定如何以及何时设置它们。
  • @davemankoff 我更新了答案,请看一下。我希望这会有所帮助。
【解决方案2】:

根据我从@MyDogTom 的answer 中得到的提示,引入我们的第一个子组件就是解决方案。

等待构造S,直到构造完Foobar。然后初始化一个子组件,将S传递给它的Builder,并使用子组件构造Other

@Subcomponent
public interface MySubComponent {
    @Subcomponent.Builder
    interface Builder {
        @BindsInstance Builder s(S s);
        StatusBarComponent build();
    }

    /**
     * Scope annotation for singleton items within the MySubComponent.
     */
    @Documented
    @Retention(RUNTIME)
    @Scope
    @interface MySubComponentScope {}

    @MySubComponentScope
    Ohter getOther();

}

// Attach MySubComponent to Dagger where appropriate, per their documentation.
// Then, in Main, do something like the following:


class Main {
  private final MySubComponent.Builder mySubComponentBuilder;
  private final S.Builder sBuilder;
  private Foobar foobar;

  @Inject
  public Main(MySubComponent.Builder mySubComponentBuilder, S.Builder sBuilder) {
    this.mySubComponentBuilder = mySubComponentBuilder;
    this.sBuilder = sBuilder;
  }

  // At some point, foobar gets created. Then we call the following method.
  private void afterInit();
    S s = sBuilder.setFoobar(foobar).build();

    Other other = mySubComponentBuilder.s(s).build().getOther();
  }
}

显然,这是一个略微做作的示例,但演示了问题的解决方案。

【讨论】:

    猜你喜欢
    • 2017-12-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-10
    • 2011-12-30
    相关资源
    最近更新 更多