在 Dagger 中实现单例生成器
Implementing a Singleton Builder in Dagger
我有一个 class 我想通过 Dagger 注入。它需要一个构建器,因为它的一些参数只在运行时才知道。我也希望这个 class 是一个单身人士。
也就是说,我们的代码库中有几部分需要这个class的实例,但实际上只有一部分代码库知道如何设置它。
设置发生在应用程序的早期 - 在任何人实际尝试使用单例之前 - 但在 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
通信,但实际上只有一部分代码可以创建它。
你可以使用 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())
}
}
更新
This specific program is an Android app; the Foobar in this case a View that is inflated, and S a controller for that View.
我强烈建议您不要保留对 View
的引用。这可能会产生细微的错误和内存泄漏。相反,我建议您引入某种事件总线,它将成为 Dagger 图中的单例,并在 S
(视图控制器)和消费者之间共享。该偶数总线将用于消费者与 S
(视图控制器)之间的通信。
建立在我从@MyDogTom 的 中得到的提示的基础上,引入我们的第一个子组件就是解决方案。
等到 S
构建完成后才构建 S
。然后初始化一个子组件,将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();
}
}
显然,这是一个稍微做作的示例,但它演示了问题的解决方案。
我有一个 class 我想通过 Dagger 注入。它需要一个构建器,因为它的一些参数只在运行时才知道。我也希望这个 class 是一个单身人士。
也就是说,我们的代码库中有几部分需要这个class的实例,但实际上只有一部分代码库知道如何设置它。
设置发生在应用程序的早期 - 在任何人实际尝试使用单例之前 - 但在 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
通信,但实际上只有一部分代码可以创建它。
你可以使用 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())
}
}
更新
This specific program is an Android app; the Foobar in this case a View that is inflated, and S a controller for that View.
我强烈建议您不要保留对 View
的引用。这可能会产生细微的错误和内存泄漏。相反,我建议您引入某种事件总线,它将成为 Dagger 图中的单例,并在 S
(视图控制器)和消费者之间共享。该偶数总线将用于消费者与 S
(视图控制器)之间的通信。
建立在我从@MyDogTom 的
等到 S
构建完成后才构建 S
。然后初始化一个子组件,将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();
}
}
显然,这是一个稍微做作的示例,但它演示了问题的解决方案。