Swappable modules with Dagger 2 - java

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.

Related

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

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 won't intercept annotated methods

I have a situation where Guice is working for some bindings, and not at all for others. Clearly I am using the API incorrectly.
In part, it's probably because I'm trying to get too "fancy" with how I'm using Guice. I've created an inheritance tree of Modules and I think I've gotten too clever (or foolish!) for my own good.
Before you look at the code below, just please understand my intention, which was to provide a reusable Module that I can place in a JAR and share across multiple projects. This abstract, reusable Module would provide so-called "default bindings" that any implementing Module would automatically honor. Things like an AOP MethodInterceptor called Profiler, which looks for methods annotated with #Calibrated and automatically logs how long it took for that method to execute, etc.
Observe the following:
#Target({ ElementType.METHOD })
#RetentionPolicy(RetentionPolicy.RUNTIME)
#BindingAnnotation
public #interface Calibrated{}
public class Profiler implement MethodInterceptor {
#Override
public Object invoke(MethodInvocation arg0) throws Throwable {
// Eventually it will calculate and log the amount of time
// it takes an intercepted method to execute, hence "Profiler".
System.out.println("Intercepted!");
return arg0.proceed();
}
}
public abstract class BaseModule implements Module {
private Binder binder;
public BaseModule() {
super();
}
public abstract void bindDependencies();
#Override
public void configure(Binder bind) {
// Save the binder Guice passes so our subclasses can reuse it.
setBinder(bind);
// TODO: For now do nothing, down the road add some
// "default bindings" here.
// Now let subclasses define their own bindings.
bindDependencies();
}
// getter and setter for "binder" field
// ...
}
public abstract class AbstractAppModule extends BaseModule {
/* Guice Injector. */
private Injector injector;
// Some other fields (unimportant for this code snippet)
public AbstractAppModule() {
super();
}
// getters and setters for all fields
// ...
public Object inject(Class<?> classKey) {
if(injector == null)
injector = Guice.createInjector(this);
return injector.getInstance(classKey);
}
}
So, to use this small library:
public class DummyObj {
private int nonsenseInteger = -1;
// getter & setter for nonsenseInteger
#Calibrated
public void shouldBeIntercepted() {
System.out.println("I have been intercepted.");
}
}
public class MyAppModule extends AbstractAppModule {
private Profiler profiler;
// getter and setter for profiler
#Override
public void bindDependencies() {
DummyObj dummy = new DummyObj();
dummy.setNonsenseInteger(29);
// When asked for a DummyObj.class, return this instance.
getBinder().bind(DummyObj.class).toInstance(dummy);
// When a method is #Calibrated, intercept it with the given profiler.
getBinder().bindInterceptor(Matchers.any(),
Matchers.annotatedWith(Calibrated.class),
profiler);
}
}
public class TestGuice {
public static void main(String[] args) {
Profiler profiler = new Profiler();
MyAppModule mod = new MyAppModule();
mod.setProfiler(profiler);
// Should return the bounded instance.
DummyObj dummy = (DummyObj.class)mod.inject(DummyObj.class);
// Should be intercepted...
dummy.shouldBeIntercepted();
System.out.println(dummy.getNonsenseInteger());
}
}
This is a lot of code so I may have introduced a few typos when keying it all in, but I assure you this code compiles and throws no exceptions when ran.
Here's what happens:
The #Calibrated shouldBeIntercepted() method is not intercepted; but...
The console output shows the dummy's nonsense integer as...29!!!!
So, regardless of how poor a design you may think this is, you can't argue that Guice is indeed working for 1 binding (the instance binding), but not the AOP method interception.
If the instance binding wasn't working, then I would happily revisit my design. But something else is going on here. I'm wondering if my inheritance tree is throwing the Binder off somehow?
And I've verified that I am binding the interceptor to the annotation correctly: I created another package where I just implement Module (instead of this inheritance tree) and use the same annotation, the same Profiler, and it works perfectly fine.
I've used Injector.getAllBindings() to print out the map of all my MyAppModule's bindings as Strings. Nothing is cropping up as the clear source of this bug.
Interception only works on Objects created by Guice (see "Limitations" http://code.google.com/p/google-guice/wiki/AOP#Limitations). You are using "new" to create the DummyObj, so no matter how clever your Module is Set up, the instance is created Outside guice.
Here's a little snipplet based on your coding. (I use your Calibrated Annotation, but had everything else in one class. You should have a look at "AbstractModule". It saves a lot of manual stuff you did with your Module-Hierarchy.
public class MyModule extends AbstractModule implements MethodInterceptor {
#Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
System.out.println("Intercepted#invoke!");
return methodInvocation.proceed();
}
#Override
protected void configure() {
bind(Integer.class).annotatedWith(Names.named("nonsense")).toInstance(29);
bindInterceptor(Matchers.any(), Matchers.annotatedWith(Calibrated.class), this);
}
public static void main(String... args) {
Dummy dummy = Guice.createInjector(new MyModule()).getInstance(Dummy.class);
dummy.doSomething();
System.out.println(dummy.getNonsense());
}
}
And my Dummy:
public class Dummy {
#Inject
#Named("nonsense")
private int nonsense;
public int getNonsense() {
return nonsense;
}
public void setNonsense(int nonsense) {
this.nonsense = nonsense;
}
#Calibrated
public void doSomething() {
System.out.println("I have been intercepted!");
}
}
So you see? I never use the word "new" (except for the Module ....). I let Guice handle the Dummy-Object and just configure the value for the nonsense int, which is then injected.
Output:
Intercepted#invoke!
I have been intercepted!
29

Does GIN support anything like child injectors?

I have one application that contains sub-applications. I would like to segregate the GIN injection so that each sub-application can have separate instances of the same core shared classes. I also want the injector to supply classes from some core modules to all sub-applications, so that singleton instances can be shared. e.g.
GIN Modules:
Core - shared
MetadataCache - one per sub-application
UserProvider - one per sub-application
In Guice I can do this using createChildInjector, but I can't see an obvious equivalent in GIN.
Can I achieve something similar in GIN?
I solved this thanks to the link given by #Abderrazakk, but as the link isn't very forthcoming with instructions I thought I'd add a sample solution here too:
Private GIN modules allow you to have a single level of hierarchical injection, where types registered inside a private module are only visible to other instances created within that module. Types registered inside any non-private modules are still available to all.
Example
Let's have some sample types to inject (and inject into):
public class Thing {
private static int thingCount = 0;
private int index;
public Thing() {
index = thingCount++;
}
public int getIndex() {
return index;
}
}
public class SharedThing extends Thing {
}
public class ThingOwner1 {
private Thing thing;
private SharedThing shared;
#Inject
public ThingOwner1(Thing thing, SharedThing shared) {
this.thing = thing;
this.shared = shared;
}
#Override
public String toString() {
return "" + this.thing.getIndex() + ":" + this.shared.getIndex();
}
}
public class ThingOwner2 extends ThingOwner1 {
#Inject
public ThingOwner2(Thing thing, SharedThing shared) {
super(thing, shared);
}
}
Create two private modules like this (using ThingOwner2 for the second one):
public class MyPrivateModule1 extends PrivateGinModule {
#Override
protected void configure() {
bind(Thing.class).in(Singleton.class);
bind(ThingOwner1.class).in(Singleton.class);
}
}
And a shared module:
public class MySharedModule extends AbstractGinModule {
#Override
protected void configure() {
bind(SharedThing.class).in(Singleton.class);
}
}
Now register the two modules in our injector:
#GinModules({MyPrivateModule1.class, MyPrivateModule2.class, MySharedModule.class})
public interface MyGinjector extends Ginjector {
ThingOwner1 getOwner1();
ThingOwner2 getOwner2();
}
Finally we can look and see that both ThingOwner1 and ThingOwner2 instances have the same SharedThing instance from the shared module, but different Thing instances from their private registrations:
System.out.println(injector.getOwner1().toString());
System.out.println(injector.getOwner2().toString());
Here it is on SOF http://code.google.com/p/google-gin/wiki/PrivateModulesDesignDoc.
Hope it helps you out.

Categories