How to use #Scope in Dagger 2 - java

I read a lot of material on the #Scope annotation but still not fully understanding if what I know is correct.
Can someone explain what exactly is #Scope for? Where is it used? How is it used?
Is it only used when defining the component?
Also is a scope defined like this?:
#Scope
public #interface SomeScope {
}
And used like this?:
#SomeScope
#Component(modules = {
HeaterModule.class,
PumpModule.class
})
public interface MainActivityComponent {
void inject(MainActivity mainActivity);
CoffeeMaker maker();
}

The definition of scopes for Java EE applications states the following:
For a web application to use a bean that injects another bean class,
the bean needs to be able to hold state over the duration of the
user’s interaction with the application. The way to define this state
is to give the bean a scope.
Let's associate it with Android. Scopes impose certain limitations in terms of dependencies lifecycle provided by a scoped provider.
For example, let's say we want to be provided with #Singleton dependency obtainable from #ActivityScoped component. The component lives as long as the Activity. Once activity is destroyed and created again, our component is instantiated accordingly and our '#Singleton' dependency is also created once again.
Summing up, our #Singleton dependencies live as long as the #Components they are associated with - this is I think most practical explanation.
Internally, once Dagger notifies within the #Component the provision method with the specified scope, it creates a ScopedProvider for dependency provision. Otherwise, a Factory is created.
The ScopedProvider's get method is a double-check singleton method:
public T get() {
// double-check idiom from EJ2: Item 71
Object result = instance;
if (result == UNINITIALIZED) {
synchronized (this) {
result = instance;
if (result == UNINITIALIZED) {
instance = result = factory.get();
}
}
}
return (T) result;
}
To see a practical #Scope usage you can take a look at one of the examples of mine:
https://github.com/dawidgdanski/AccountAuthenticatorExample
Hope, that was helpful somehow.
EDIT 1:
The article provided by #Fshamri explains it even better.
EDIT 2:
Let's consider the following structure :
The Component:
#ActivityScope //Dagger ignores the annotation put atop the #Component. I put it there just for readability
#Component(dependencies = DependencyGraph.class,
modules = {ActivityModule.class})
public interface ActivityComponent {
void inject(MainActivity mainActivity);
void inject(SignUpActivity signUpActivity);
void inject(SignInActivity signInActivity);
And the module supplying the dependencies:
#Module
public class ActivityModule {
private final Activity activity;
public ActivityModule(Activity activity) {
this.activity = activity;
}
#Provides
#ActivityScope
MainView provideMainView() {
return (MainView) activity;
}
#Provides
SignInView provideSignInView() {
return (SignInView) activity;
}
}
The #ActivityScope extends the #Scope annotation - it is interpreted by Dagger the same way as the #Singleton is. The ActivityModule provides 2 views: the #ActivityScoped MainView and the SignInView without any scope. During the compilation time Dagger's annotation processor creates Providers for both views. The difference between the MainView's and the SignInView's generated Provider is that for the MainView Dagger generates the ScopedProvider (because we explicitly request this kind of provider with #ActivityScope) whereas for the SignInView Dagger generates regular Factory provider.
The contract of the ScopedProvider looks like the one above. The contract for the Factory provider, however, looks the following way:
#Generated("dagger.internal.codegen.ComponentProcessor")
public final class ActivityModule_ProvideSignInViewFactory implements Factory<SignInView> {
private final ActivityModule module;
public ActivityModule_ProvideSignInViewFactory(ActivityModule module) {
assert module != null;
this.module = module;
}
#Override
public SignInView get() {
SignInView provided = module.provideSignInView();
if (provided == null) {
throw new NullPointerException("Cannot return null from a non-#Nullable #Provides method");
}
return provided;
}
public static Factory<SignInView> create(ActivityModule module) {
return new ActivityModule_ProvideSignInViewFactory(module);
}
}
Conclusions:
1. The ScopedProvider is a classical double-check singleton pattern. It guarantees to create only one instance of the dependency.
2. The Factory's get() method just distributes the dependency from the module which means it can distribute new instance of the dependency each time it is requested (in fact, in the example the Activity is only cast to the SignInView which still gives us a single instance but this is copy-pasted logic from my example).
3. Dagger cares about #Scopes provided along with provision methods in the #Modules, not the #Components.
I encourage you to download the sample and build the project, then to see the contract of the generated Dagger components and modules.
Hope this is more understandable now.

have you looked at this article?
In Dagger 2 scopes mechanism cares about keeping single instance of
class as long as its scope exists. In practice it means that instances
scoped in #ApplicationScope lives as long as Application object.
#ActivityScope keeps references as long as Activity exists (for
example we can share single instance of any class between all
fragments hosted in this Activity). In short - scopes give us “local
singletons” which live as long as scope itself.

Related

How to resolve a circular dependency while still using Dagger2?

I have two classes, Foo<T> and Bar, which depend on each other, as well as various other classes. I am using Dagger-2 for dependency injection, but if I naively add the circular dependency, Dagger hits a stack overflow at runtime. What's a good way to refactor the classes to fix this, while still using Dagger to inject all the other dependencies, and with minimal duplication and changes to existing calls?
The easy way out is to use Lazy<T> on one side.
Lazy<Foo> foo;
#Inject
Bar(Lazy<Foo> foo) {
this.foo = foo;
}
// use foo.get(); when needed
After an excessive amount of thought and talks with coworkers, we ended up doing the following:
class Foo<T> extends FooWithoutDep<T> {
#Inject Foo(Bar bar, OtherDep1 dep1, OtherDep2 dep2) {
super(dep1, dep2);
setBarDep(bar);
}
}
class FooWithoutDep<T> {
//Field declarations elided
#Inject FooWithoutDep(OtherDep1 dep1, OtherDep2 dep2) {
//Normal constructor stuff
}
void setBarDep(Bar bar) { this.bar = bar; }
//The rest of the actual logic
}
class Bar {
//Field declarations elided
#Inject Bar(FooWithoutDep<Thing> foo, OtherDep3 dep3) {
this.foo = foo;
this.foo.setBarDep(this);
this.dep3 = dep3;
}
//Code that uses Foo and the other dependencies
}
Explaining this -- we moved the actual logic of Foo into a parent class (FooWithoutDep), that took the circular dependency as a settable field rather than a constructor parameter. Then the original class just contained a constructor that took the circular dependency and called the setter. The other class, Bar, depended on the parent (FooWithoutDep), and called the setter explicitly, passing itself (this). This enables all the existing references to the class to remain the same, while still using Dagger to inject all the dependencies.
This seemed confusing enough to be worth writing up here.
This is how I resolved it, without parent classes.
Class 1: Engine.
(in component interface)
#Provides
public Engine myEngine(Context context) {
return new Engine (context);
}
Class 2: Parts. Engine also needs Parts instance but the creation is delayed.
#Inject
public Parts(Context context, Engine engine) {
this.context = context;
this.engine= engine;
engine.setParts(this);
}
Circular dependency can be achieved but one class must be initiated first before the other.
Again, if possible, refactor code to avoid circular DI.

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: Inject a class bound by another module?

I have the following classes:
public class CacheModule extends AbstractModule {
#Override
protected void configure() {
bindConstant().annotatedWith(Names.named(TIMEOUT")).to(60);
// ...etc.
}
}
public class DefaultCacheAdaptor implements CacheAdaptor {
private CacheModule bootstrapper = new CacheModule();
#Named("TIMEOUT") private int timeout;
// other fields omitted for brevity
public DefaultCacheAdaptor() {
super();
Injector injector = Guice.createInjector(bootstrapper);
#Named("TIMEOUT") int t = injector.getInstance(Integer.class);
setTimeout(t);
}
}
public class QueueModule extennds AbstractModule {
#Override
public void configure() {
bind(CacheAdaptor.class).to(DefaultCacheAdaptor.class);
}
}
public class DefaultQueueAdaptor implements QueueAdaptor {
private QueueModule bootstrapper = new QueueModule();
private CacheAdaptor cacheAdaptor;
public DefaultQueueAdaptor() {
super();
Injector injector = Guice.createInjector(bootstrapper);
setCacheAdaptor(injector.getInstance(CacheAdaptor.class));
}
}
The CacheModule/CacheAdaptor/DefaultCacheAdaptor is located in a different JAR than QueueModule/QueueAdaptor/DefaultQueueAdaptor, and so the latter JAR depends on the former JAR at runtime (obviously).
The purpose of coding things this way is to allow the CacheModule to boostrap/inject the entire object graph under DefaultCacheAdaptor when the user writes:
CacheAdaptor cacheAdaptor = new DefaultCacheAdaptor();
Ditto for the QueueAdaptor.
It just so happens to be that the QueueAdaptor gets injected with a CacheAdaptor.
However, DefaultCacheAdaptor is the "root" of its own object tree, and should always be injected by the CacheModule.
So I ask: how can I bind DefaultCacheAdaptor to CacheAdaptor from inside QueueModule, but ensure that the DefaultCacheAdaptor is itself initialized/bootstrapped by the CacheModule?
To be honest it sounds like this problem isn't actually one of Guice, but instead the standard problem across software engineering: ensuring your dependencies do what they claim to do. QueueModule shouldn't concern itself about whether DefaultCacheAdaptor holds up to its general contract. Instead, write a unit test for DefaultCacheAdaptor that guarantees that it bootstraps itself, then use it in QueueModule without a second thought.
This is especially true because DefaultCacheAdaptor has a completely unrelated injector tree. You should be able to use DefaultCacheAdaptor opaquely, and stop concerning QueueAdaptor with its implementation details. Its bootstrapping is part of its implementation, not its API.
Even if you were to merge the two Injector graphs into one (by injecting the Injector and calling createChildInjector, for instance), there's very little way to guarantee at compile time that bindings you need in other modules will exist, because Modules work at runtime. Your best bet is to write a unit test. You can fail a little faster by calling requireBinding, which will fail at Injector creation if that particular dependency doesn't end up satisfied externally.

Categories