Reputation: 17779
I have a class that I would like to inject via Dagger. It requires a builder, as some of its parameters are only known at runtime. I would also like this class to be a singleton.
That is to say, several parts of our codebase require the instance of this class, but only one part of the codebase actually knows how to set it up.
The setup happens early on in the application - before anyone would actually try to use the singleton - but after Dagger has already initialized its root component.
What is the right way for other parts of the code to get access to the object?
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? */) {
}
Edit:
For clarity, let me state that Foobar in this example is created early in the application, but after Dagger and the high level structure of the app has been instantiated. This specific program is an Android app; the Foobar
in this case a View that is inflated, and S
a controller for that View.
There will only ever be one Foobar
and one S
. Various parts of our code want to be able to communicate with S
, but only one part of our code can actually create it.
Upvotes: 1
Views: 535
Reputation: 17779
Building off of a hint I gleamed from @MyDogTom's answer, introducing our first Subcomponent was the solution.
Wait to construct the S
until after Foobar
has been constructed. Then initialize a subcomponent, passing S
to its Builder, and use the subcomponent to construct 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();
}
}
This is a slightly contrived example, obviously, but demonstrates the solution to the problem.
Upvotes: 0
Reputation: 4606
You can use BindsInstance
for that https://dagger.dev/api/2.10/dagger/BindsInstance.html
Your Application creates the RootComponent
. During creation it should provide the instance of the Foobar
. Let's take a look at the pseudocode
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())
}
}
Update
This specific program is an Android app; the Foobar in this case a View that is inflated, and S a controller for that View.
I strongly discourage you from keeping a reference to a View
. This might produce subtle bugs and memory leaks. Instead I suggest you to introduce some sort of event bus which will be a singleton in your Dagger graph and shared between S
(view controller) and consumers. This even bus will be used for communication between consumers and S
(view controller).
Upvotes: 1