How to override dependencies within Scopes in Dagger 2 - java

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

Related

Swappable modules with Dagger 2

So I have some code that runs an algorithm, say with an AlgoRunner class. Now this AlgoRunner class can be implemented in multiple ways to run different algorithms using Algo classes. I want to use Dagger 2 to provide different implementations of the AlgoRunner class to a "Manager" class that passes input to AlgoRunner as well as other components that it manages.
Question
I have the following right now, but I'm not sure if this is the correct way, mainly because of that empty AlgoRunnerProvider module. Is that any other way to achieve what I'm trying to do? Or to simplify what I have?
Should I just create different components, OneAlgoRunnerComponent and TwoAlgoRunnerComponent and inject the Manager from each of those?
The class that constructs the Manager instance uses this component to inject the AlgoRunner into that instance so that the Manager can pass it the inputs.
#Component(
modules = {
AlgoRunnerProvider.class
}
)
public interface AlgoRunnerComponent {
void inject(Manager manager);
AlgoRunner getAlgoRunner();
}
AlgoRunnerProvider Module
#Module
public class AlgoRunnerProvider {
#Provides
public AlgoRunner getAlgoRunner() {
return null;
}
}
OneAlgoRunnerProvider, that overrides the provides method in AlgoRunnerProvider.
Could have a TwoAlgoRunnerProvider as well, that does the same thing and provides TwoAlgoRunner, as long as that extends AlgoRunner.
public class OneAlgoRunnerProvider extends AlgoRunnerProvider {
private final OneAlgo algo;
public OneAlgoRunnerProvider(OneAlgo algo) {
this.algo = algo;
}
#Override
public OneAlgoRunner getAlgoRunner() {
return new OneAlgoRunner(algo);
}
}
All this is used like this right now:
AlgoRunnerComponent build = DaggerAlgoRunnerComponent.builder()
.algoRunnerProvider(new OneAlgoRunnerProvider(new OneAlgo()))
// .algoRunnerProvider(new TwoAlgoRunnerProvider(new TwoAlgo()))
.build();
Manager manager = managerComponent.getManager();
build.inject(manager);
Truth.assertThat(manager.algoRunner).isInstanceOf(OneAlgoRunner.class);
// Truth.assertThat(manager.algoRunner).isInstanceOf(OneAlgoRunner.class);
Thanks a lot!
The dagger framework is used to handle object creation for you. If you start doing some sort of initialization in one of your classes you wish to provide, there is probably something not as it is supposed to be (see getAlgoRunner()).
If you have different types that you want to provide at runtime, you want a factory of some sorts to create the correct object. Enter dagger.
You have multiple ways of achieving what you want. Basically, the module should handle the object creation:
#Module
public class AlgoRunnerProvider {
#Provides
public AlgoRunner getAlgoRunner() {
// todo create the correct type
return null;
}
}
1. #Named annotation (or some other Qualifier)
If you know at compile time which class is going to need which type, you should use qualifiers.
#Named("Version1")
#Inject
AlgoRunner mRunner;
You then just can provide different implementations from your module:
#Provides
#Named("Version1")
public AlgoRunner getAlgoRunner() {
return new Version1AlgoRunner();
}
#Provides
#Named("OtherVersion")
public AlgoRunner getAlgoRunner(Depends someOtherDependency) {
return new OtherVersionAlgoRunner(someOtherDependency);
}
2. Switching at runtime
While you could always use the first option by creating multiple classes with different dependencies, you might want to be able to chose at runtime. For this, you need to pass in some argument to your module:
#Module
public class AlgoRunnerProvider {
private final int mType;
public AlgoRunnerProvider(int type) {
mType = type;
}
#Provides
public AlgoRunner getAlgoRunner() {
if(mType == TYPE_A) {
return new Version1AlgoRunner();
} else {
return new OtherVersionAlgoRunner();
}
}
}
With this variant you still have your creation logic inside your module, where your dependencies come from.
3. Use different modules
Another approach would be to use different modules. This will only be a clean solution if determined at compile time (different classes using different modules) and not some choosing logic at runtime in the same class.
If you start writing code like if typeA then moduleA else moduleB you should probably stop and do something else.
You can use the same component but create it using different modules for different classes by using good old inheritance. Every module just provides its implementation of AlgoRunner.
// one class using one module to get a specific behavior
public class MyClassVersionA {
public void onCreate(Bundle saved) {
component.myModule(new MyModuleVersionA()).build();
}
}
// another class using a different module to get a different behavior
public class MyClassSomethingElse {
public void onCreate(Bundle saved) {
component.myModule(new MyModuleSomethingElse()).build();
}
}
You would then just subclass your module accordingly like so
// did not test the following, something like this...
public abstract class MyModule {
#Provides
public AlgoRunner getAlgoRunner();
}
public class MyModuleVersionA extends MyModule {
#Provides
#Override
public AlgoRunner getAlgoRunner() {
return new Version1AlgoRunner();
}
}
public class MyModuleSomethingElse extends MyModule {
#Provides
#Override
public AlgoRunner getAlgoRunner() {
return new SomeOtherAlgoRunner();
}
}
There are probably even more possibilities, especially if starting to mix those approaches, but I think those 3 to be the basic tools that you can use.

Why Dagger inject is not working but component.getObject yes

I am trying to use Dagger 2 for instantiating a Retrofit interface. The CloudContactDataStore class injects the RestClient and calls its methods.
When I instantiate a CloudContactDataStore object, its RestClient attribute has null value.
public class CloudContactDataStore implements ContactDataStore {
#Inject RestClient restClient;
public CloudContactDataStore() {
this.initializeInjector();
}
private void initializeInjector() {
ApiComponent component = DaggerApiComponent.builder()
.apiModule(new ApiModule())
.build();
component.inject(this); // Nothing changes, restClient is null!
this.restClient = component.getRestClient(); // This works
}
}
Here is how I create the Dagger Module and Component:
#Singleton
#Component(modules = ApiModule.class)
public interface ApiComponent {
void inject(ContactDataStore contactDataStore);
RestClient getRestClient();
}
#Module
public class ApiModule {
#Provides public RestClient provideRestClient(ApiService apiService) {
return new RestClientImpl(apiService);
}
#Provides public ApiService provideApiService(RestAdapter restAdapter) {
return restAdapter.create(ApiService.class);
}
#Provides public RestAdapter provideRestAdapter() {
return RestApiAdapter.getInstance();
}
}
So, why the inject function does not work but calling component's getRestClient() yes?
I find it's very useful to look at the code that Dagger 2 generates as it's quite easy to follow and can often point you in the right direction.
Dagger 2 creates code much like you'd write so think about how you would implement ApiComponent.inject(ContactDataStore). Assuming within that method you had access to a RestClient how would you get it into the field? If you sat and wrote it you'd notice that you have to do something like this:
((CloudContactDataStore) contactDataStore).restClient = restClient;
Or in other words you'd need to cast it down to a specific implementation. Dagger 2 doesn't cast down, EVER (at least I've not seen it), because that is usually unsafe.
So, you have two choices. Change the inject(ContactDataStore) method to inject(CloudContactDataStore), or provide a method on ContactDataStore that allows the RestClient to be passed in.
Update: Its not possible to #Inject through abstract, i.e. interface, method.
If you want to inject it through the ContactDataStore API then you have a problem as there is currently a restriction in Dagger 2 (have raised a feature request to remove it) that you cannot mark an abstract method with #Inject. So, in the meantime (or forever if there is some reason why it cannot work in Dagger 2) you will need to do it manually, i.e. get the instance of RestClient from your component method and pass it to the appropriate method on the interface.

Guice: instantiating a singleton before creating the module

Is it possible to instantiate and assign a singleton to a reference with Guice before creating the Module and pass that instance to the Module constructor be bound during configuration?
Here is an example of what I mean:
I have a method that allows me to create objects depending on a custom implementation of an interface which is being passed in constructor as an Optional (if the user won't provide a custom implementation, we will use the default one), which is being done by binding the interface to that particular implementation in the Module class. :
public static MyClass createMyClassObject(Optional<SpecialInterface> customSpecialInterfaceObject) {
SpecialInterface specialInterfacebject;
if(customSpecialInterfaceObject.isPresent() {
specialInterfaceObject = customSpecialInterfaceObject.get()
} else {
/* here I would like to bind it to an instance of the DefaultSpecialInterfaceObject but can't really do something like:
Injector injector = Guice.createInjector(myClassModule);
DefaultSpecialInterface instance = injector.getInstance(DefaultSpecialInterface.class);
as the module is yet to be created */
}
MyClassModule myClassModule = new MyClassModule(specialInterfaceObject);
Injector injector = Guice.createInjector(myClassModule);
return injector.getInstance(MyClass.class);
}
I'm currently using classes instead of instances to solve this problem, such as in the example below, but I don't quite like this solution. Would be happy to see a better way of doing it:
private static Class resolveSpecialInterfaceObject(Optional<SpecialInterface> customSpecialInterfaceObject) {
Class specialInterfaceObjectClass;
if (customSpecialInterfaceObject.isPresent()) {
specialInterfaceObjectClass= customSpecialInterfaceObject.get().getClass();
} else {
specialInterfaceObjectClass = DefaultSpecialInterface.class;
}
return specialInterfaceObjectClass;
}
public abstract class MyClassModule extends AbstractModule {
private final Class<SpecialInterface> specialInterfaceObjectClass;
public MyClassModule(Class<SpecialInterface> specialInterfaceObjectClass) {
this.specialInterfaceObjectClass= specialIntefaceObjectClass;
}
#Override
protected void configure() {
bind(SpecialInterface.class).to(specialInterfaceObjectClass);
}
}
Edit, from a comment below:
one more thing- didn't want to make the question too long; actually, I also want to perform another operation on the resulting instance of SpecialInterface, but only if it is the instance of DefaultSpecialInterface and I don't think it should be done in the Module. I was thinking if I could just have this bean up and running before, such as in Spring, so I could just pass it to the Module, but also use it in another method call before?
Can you take the whole Optional and use bind(...).toInstance(...)?
public static MyClass createMyClassObject(
Optional<SpecialInterface> customSpecialInterfaceObject) {
MyClassModule myClassModule = new MyClassModule(customSpecialInterfaceObject);
Injector injector = Guice.createInjector(myClassModule);
MyClassFactory instance = injector.getInstance(MyClassFactory.class);
return instance.createMyClassObject();
}
class MyClassModule extends AbstractModule {
private final Optional<SpecialInterface> customObject;
MyClassModule(Optional<SpecialInterface> customObject) {
this.customObject = customObject;
}
#Override public void configure() {
if (customObject.isPresent()) {
// Singleton by necessity: Guice doesn't know how to create another one.
bind(SpecialInterface.class).toInstance(customObject.get());
} else {
// Default scoped. Add ".in(Singleton.class)" if necessary.
bind(SpecialInterface.class).toInstance(DefaultSpecialInterfaceClass.class);
}
}
}
If you want to perform additional initialization on DefaultSpecialInterface and nothing else, you have a number of options:
If some kind of initialization is important for all implementations and likely too heavy to put into a class constructor, add an initialize method on your SpecialInterface. Make the custom one a no-op, and implement it for DefaultSpecialInterface.
If the initialization is unique to DefaultSpecialInterface, I see no reason why it shouldn't be in the Module. Write a #Provides method or bind to a Provider<SpecialInterface> that creates and initializes DefaultSpecialInterface correctly.
If your real goal is to keep the business logic out of a Module, you can do so by extracting it into a free-standing Provider or DefaultSpecialInterfaceFactory that is responsible for that.
Remember, Guice is responsible for feeding fully-constructed objects into your object graph, and that means that injecting a SpecialInterface should get a ready-to-use implementor of the SpecialInterface general contract. If Guice needs to perform some initialization to make that happen, it's not unreasonable to have it do so, and a Module isn't a bad place to do it.

Dagger 2 activity injection not working

I'm trying the new dagger 2, it's my first time implementing it but I can't make it work. I think I got the concept and I understand the example here
I try to copy the same structure just a bit modified for my example.
Here is the AppComponent that extends the Graph where I defined the classes I want.
#ApplicationScope
#Component(modules = {AppModule.class, DataModule.class})
public interface EFAppComponent extends EFGraph {
/**
* An initializer that creates the graph from an application.
*/
public final static class Initializer {
private Initializer() {
} // No instances.
public static EFAppComponent init(EFApp app) {
return Dagger_EFAppComponent.builder()
.appModule(new AppModule(app))
.build();
}
}
}
public interface EFGraph {
public void inject(EFApp app);
public ImageLoader imageLoader();
public EventBus eventBus();
}
Then inside each module I'm providing the corresponding classes. From here everything works good and Dagger seams to build the Dagger_EFAppComponent correctly.
Then in the Application class I init using the constructor
component = EFAppComponent.Initializer.init(this);
component.inject(this);
Then my goal is to Inject ImageLoader and EventBus in my activity. To do that I create a ActivityComponent.
#ActivityScope
#Component(
dependencies = EFAppComponent.class,
modules = ActivityModule.class
)
public interface ActivityComponent {
public void inject(BaseActivity activity);
}
Then from my activity I call inject.
activityComponent = Dagger_ActivityComponent.builder()
.eFAppComponent(component)
.activityModule(new ActivityModule(this))
.build();
activityComponent.inject(this);
So because I declared #Inject EventBus eventBus in my activity after the inject method call should be injected. Well it's not.
So after debugging and tracking step by step my app and the example I realized that the Dagger_ActivityComponent is not build correctly.
private final ActivityModule activityModule;
private final EFAppComponent eFAppComponent;
private Dagger_ActivityComponent(Builder builder) {
assert builder != null;
this.activityModule = builder.activityModule;
this.eFAppComponent = builder.eFAppComponent;
initialize();
}
Were the initialize method is empty and no Provider are declared as variable.
Am I missing something? I've been the full day trying to make it work but I'm not success.
Appreciate the help.
Well... After the full day spend on this I decide to post it and after 5 minutes I found the solution.
I'm using a BaseActivity for all my activity, and I thought I could use this to inject my components but seams this is not possible.
So I just change this line in my ActivityComponent
public void inject(BaseActivity activity)
to
public void inject(MainActivity activity)
So to the name of the activity where I'm injecting the dependencies...
What brings me to a new question. How can I implement a base component to handle all my activities? or I must create a Component for each view/activity/fragment I want to have DI?
#Marcel, I recently migrated a project from Dagger 1.2 over to 2.0. I had similar solution where abstract BaseActivity injected the dependencies by doing something along the lines of "application.inject(this);". With Dagger 2.0 this is not possible (as of now) as mentioned in the comment above.
One of the ways I got around this was by changing the "inject" method in the Application class itself to strong type the object. For Example..
class MyApp extends Application {
...
public void inject(Object activity) {
if (activity instanceof MainActivity) {
mComponent.inject((MainActivity) activity);
} else if (activity instanceof SubActivity) {
mComponent.inject((SubActivity) activity);
}
}
}
With that I did not have to change anything in my existing classes.
FYI: There is a project called Bullet by #thomas-broyer that does this too.

Guice runtime dependency parameters reinjection

A question about Guice. I'm still learning it, but I can understand the fundamentals.
This question was already asked a couple of times on the net, but never with a concrete answer(none that I could find).
Say I have a situation like on the picture(a similar example was somewere on the net).
public class Dog {}
public class Walk implements Walkable {
private final Dog dog;
private final boolean leash;
#Inject
public Walk(Dog dog, #Assisted boolean leash) {
this.dog = dog;
this.leash = leash;
}
public void go() {
}
}
public interface Walkable {
void go();
}
public interface WalkFactory {
Walk create(boolean leash);
}
public class AssistedMain {
public static void main(String[] args) {
Injector i = Guice.createInjector(new AbstractModule() {
protected void configure() {
install(new FactoryModuleBuilder().
implement(Walkable.class, Walk.class).
build(WalkFactory.class));
}
});
Walk walk = i.getInstance(WalkFactory.class).create(true);
}
}
That's all great. But the question is - can I, somehow, reinject that object instance to the "container"(injector) to be used on the classes that rely on this dependency.
So, lets add a interface Person, class PersonImpl.
The new classes source are:
public interface Person {
void walkDog();
}
public class PersonImpl implements Person {
private Walkable walkable;
#Inject
public PersonImpl(Walkable walkable) {
this.walkable = walkable;
}
public void setWalkable(Walkable walkable) {
this.walkable = walkable;
}
public void walkDog() {
walkable.go();
}
}
So, the question is - am I, somehow able to actually inject this particular instance into the added object. This is a simple example, but we can presume there are 10 levels of classes below this one.
The solution I found is not very flexible. Something like:
Injector i = Guice.createInjector(new SimpleModule(false, dog));
And then bind to concrete instance. That's not very dynamic. Basically, every time I need a different runtime/dynamic parameter I have to recreate the injector.
The Provider<T> is nice, the FactoryModuleBuilder helps, but how can I inject the objects back?
Are there more dynamic solutions to this problem?
Thanks.
MPierce - agreed. Ill try to explain the way i visualized the problem(you can correct me if im wrong).
Being originaly derived from a "service locator" pattern, the idea that it can manage more than services is optimistic to say the least.
We could split the application into Service and Data classes, or you could say that we have application and infrastructure code - "Dependency Injection", a great book.
So, basicly, dependecy injection, and dependency injection frameworks in general are great. For solving infrastructure, or "service" code.
Any dynamic(runtime) parameters being injected into the Container/Injector are basicly forcing you to end the object graph.
For example, we have the folowing design:
EmailMessage is a runtime parameter. It can be "injected" into email service outside the Container/Injector, but it ends the object graph. If we want to request EmailDispatcher, after we injected the EmailMessage into EmailService(which is, I repeat, done outside injector), we could no longer fetch EmailDispatcher from the injector.
Then, you could redesign your model so it "fits" into the Container/Injector concept of dynamic parameters.
But then again, you forced the design, and suddenly, EmailDispatcher has too many responsibilites. It could be used in such a context, where you dont have many infrastructure classes.
And when you have a design like you have in the third example picture, you cannot use the Injector/Container to fetch you a NextService3 instance(nor any below the level of EmailDispatcher).
The problem being - if you have any dynamic(runtime) parameters, you can only use dependency injection for classes above the class that requires a dynamic parameter, you can forget the classes below.
Phew.
Correct?
Part of the problem depends on how you're resolving that 'false' is the thing you want to set for the leash field. Is that coming from config data or what?
A provider method may be helpful...
class FooModule extends AbstractModule {
...
#Provides
Walkable getWalkable(Dog dog) {
boolean leash = getBooleanFromSomewhere();
return new Walk(dog, leash);
}
}
If you can clarify where that boolean is coming from, it'll help me to understand what type of approach is applicable.
You can use custom scopes, much like when using guice servlets. That way you can create your instance, and then seed it in the injector.

Categories