Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to override dependencies within Scopes in Dagger 2

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

like image 808
Addev Avatar asked Nov 20 '15 10:11

Addev


People also ask

What does @singleton annotation do?

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.

How does the custom scope work in Dagger?

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).

Can a subcomponent have multiple parents?

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).

What is subcomponent in Dagger?

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.


1 Answers

The short answer is...you can't do that.

With dagger everything is done at compile time.

  1. 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.

  2. 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.

like image 116
David Medenjak Avatar answered Oct 25 '22 07:10

David Medenjak