在 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();
  }
}

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