How can you override dependencies within different scopes of Dagger 2? Example:
I have two components in my app: ApplicationComponent
and ActivityComponent
. ApplicationComponent
is the base component and ActivityComponent
is a scoped component where I want to perform the override.
For this example I created those models:
public class Parrot {
private final HelloPrinter helloPrinter;
public Parrot(HelloPrinter helloPrinter) {
this.helloPrinter = helloPrinter;
}
public void sayHello(){
helloPrinter.print();
}
}
public interface HelloPrinter {
void print();
}
public class AppHelloPrinter implements HelloPrinter{
@Override
public void print() {
System.out.println("Hello Application");
}
}
public class ActivityHelloPrinter implements HelloPrinter {
@Override
public void print() {
System.out.println("Hello Activity");
}
}
And the code:
ApplicationComponent applicationComponent = DaggerApplicationComponent.builder().build();
applicationComponent.provideParrot().sayHello();
activityComponent = DaggerActivityComponent.builder()
.applicationComponent(applicationComponent).build();
activityComponent.provideParrot().sayHello();
My desired output is:
Hello Application
Hello Activity
So I made the modules:
ApplicationModule:
@Singleton
@Component(modules = ApplicationModule.class)
public interface ApplicationComponent {
Parrot provideParrot();
}
@Module
public class ApplicationModule {
@Provides
@Singleton
HelloPrinter providePrinter(){
return new AppHelloPrinter();
}
@Provides
Parrot provideParrot(HelloPrinter helloPrinter) {
return new Parrot(helloPrinter);
}
}
ActivityModule: Attempting to override the HelloPrinter
@PerActivity
@Component(dependencies = ApplicationComponent.class, modules = ActivityModule.class)
public interface ActivityComponent {
Parrot provideParrot();
}
@Module
@PerActivity
public class ActivityModule {
@Provides
@PerActivity
HelloPrinter provideHelloPrinter() {
return new ActivityHelloPrinter();
}
}
But with this config the output is:
Hello Application
Hello Application
What do I am doing wrong? Thanks
The @Singleton annotation is used to declare to Dagger that the provided object is to be only initialized only once during the entire lifecycle of the Component which uses that Module.
A scope is an annotations class with specific additional annotations: @Scope and @Retention. @Scope annotation is provided by Dagger library to define custom scopes. In our example, we create two scopes: @ActivityScope (for activities) and @FragmentScope (for fragments).
Note that a subcomponent can only have one parent. you cannot specify your parent actually. Your parent (or the parent of its parent) should ensure that it has all its child's dependency (aside from the modules of course).
In Dagger 2, component is the main container-like object that binds all the dependencies (or it's factory). Subcomponent are components that is like an extension to its parent component. It can be used for. Partition the dependencies into different compartments.
The short answer is...you can't do that.
With dagger everything is done at compile time.
You have an application component, that knows how to construct a HelloPrinter
and a Parrot
.
You then expose the Parrot
for all Components to use.
You have your activity component, that also knows how to construct a HelloPrinter
!
So what happens?
Keep in mind the object graph. Components know what they can build and depend on other components, exposing known objects themselves.
applicationComponent.provideParrot().sayHello();
This one is easy. You create the component, you want a parrot and it is constructed using the known Printer.
activityComponent.provideParrot().sayHello();
What happens here, is (basically) the same. You say you want a parrot. Your activity component does not know how to make one, it just knows how to make a printer!
But wait. It has a dependency on an application component, conveniently exposing a Parrot
Factory.
The application components factory gets called and the parrot gets instantiated. Since the application module knows how to build a printer, it uses the one at hand.
...now what
So...you could provide Parrots in your activity component, they then would use a different printer!
Gradle: error: Parrot is bound multiple times
Here we would get 2 Parrots into our object graph, since there is no "overwriting" happening. This won't work, and shouldn't.
Conclusion
There is no way to override methods. As soon as you declare a second Parrot
or HelloPrinter
it will fail compilation.
The only possiblity to achieve a similar functionality would be to use @Named()
annotations on which printer to use and / or pull the whole parrot creation down into the activity module.
And please correct me if I am missing something, but I don't even see a way to keep the signature the same with using named annotations.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With