May I know the difference between #Singleton VS static Provides in dagger2?
#Provides static User currentUser(AuthManager authManager) {
return authManager.currentUser();
}
#Provides #Singleton User currentUser(AuthManager authManager) {
return authManager.currentUser();
}
These are very different attributes, and you can have one or the other independently. All of these are valid:
#Provides User currentUser(...) {}
#Provides static User currentUser(...) {}
#Provides #Singleton User currentUser(...) {}
#Provides #Singleton static User currentUser(...) {}
To set the stage, a #Provides User method says "for this Component or its dependencies, call this #Provides method every time you need a User". Typically the method will return a new instance every time, and Dagger won't save or cache the instance.
#Singleton is an example of a scope, which is a fancy way to say lifecycle policy or policy for how often to create a new instance. #Provides #Singleton User says "for this Component or dependencies, just call this #Provides method once, and save the result". #Singleton happens to be a built-in common case, but you could also imagine creating a #UserScope (always return the same instance for this User), or in Android a #FragmentScope or #ActivityScope.
For your specific case, you probably don't want #Singleton, because it would instruct your component to save or cache the value from AuthManager. If the User value may change across your application's lifetime, the Component wouldn't reflect that. (In that case you would also want to make sure to inject Provider<User>, which would update, rather than User which would not.)
Leaving scopes behind for a moment, static behaves exactly the way you would expect it to in Java: If a method doesn't require any instance state, you can make it static, and your virtual machine can call it without preparing any instance state. In your generated Component implementation, Dagger will automatically call static methods statically, and instance methods on the Module instance you pass into your Component; in Android this results in a sizable performance increase. Because you don't use any instance state in your currentUser method, it can easily be made static.
Further reading:
SO: Scopes in Dagger 2
Dagger docs: Component (see heading "Scope")
With #Singleton annotation only one instance of User object will be created throughout the application lifecycle.
static on #Provides methods introduced recently to make the invocation of method faster by 15 to 20% as mentioned here. There will be multiple instances of User object if we call this method multiple times.
Related
Greeting all!
Please help me to figure out if the scenario, I need to cope with, which meets the Dagger concepts.
I have to inject a class into my Activity which needs this activity to be created. The only way comes to my mind is to add the activity to the Model and use it in the object Provides method. does it sound like a normal usage scenario.
#Module
public class SampleDiModule {
private Activity activity;
public SampleDiModule(Activity activity){
this.activity = activity;
}
#Provides
#ModuleScope
public InjectedObject provideInjectedObject(){
return new InjectedObject.createForAcivity(activity)
}
}
My intention to inject an activity presenter, the presenter depends on a object which could be created only by the object factory in the following way
public MyPresentor(InjectedObject object){
}
InjectedObject object = InjectedObjectFactory.forActivity(this)
Thanks
Ok, there are multiple ways to achieve this.
Two of them are on top of my mind.
First, using Dagger scopes. You can create #Activity scope which will manage objects that have life bound to life of particular activities. When creating dagger module that is scoped to Activity, you can pass an activity reference as constructor parameter, and then use it. Something like this (in pseudocode):
class ActivityScopedModule {
ActivityScopedModule(Activity: activity) {
this.activity = activity;
}
CustomObject provideCustomObject() {
return new CustomObject(this.activity);
}
}
Important thing is that modules that are scoped to activity must be instantiated from Activity.onCreate() Here you can find more about creating dagger scopes: http://frogermcs.github.io/dependency-injection-with-dagger-2-custom-scopes/ Or in this three-part series: https://android.jlelse.eu/dagger-2-part-i-basic-principles-graph-dependencies-scopes-3dfd032ccd82
Another approach, the easier one, would be to, instead of using static InjectedObjectFactory.forActivity(this), to make it as non-static class InjectedObjectFactory, make it's instance in dagger module as new InjectedObjectFactory() and then from activity you call myInjectedObjectFactory.forActivity(this) and pass it to presenter.
I am new to Dagger (version 2.16 on Android) and based on my reading so far, I understand that for a component, there should be a provider (#Provides or #Binds) encapsulated in a module (#Module). Going through a lot of samples, I see code which has some objects which are not offered in any Module, nor are they being instantiated using new.
It is also my understanding that in order to access the dependencies in a module, a consumer class needs to inject itself in the component graph (components usually offer a method to inject classes). The code examples are not doing this either.
Here's some code demonstrating both my concerns. RecipePresenter is not being provided in any module, but still RecipeActivity is using it.
A possible explanation I could think of is that #Inject, in addition to requesting the dependency also adds/injects the requesting class (RecipePresenter in the linked code) into the component graph. But assuming there are multiple components/subcomponents, which component does the class using #Inject constructor gets attached to? If my understanding is correct, why do activities and fragments have to inject themselves manually in the component - shouldn't they be auto-injected if they declare a variable annotated with #Inject?
RecipePresenter has an #Inject-annotated constructor, which allows it to be provided. The #Inject annotation on the recipePresenter field within RecipeActivity does not help, just the #Inject-annotated constructor.
class RecipePresenter #Inject constructor(
private val useCase: RecipeUseCase,
private val res: StringRetriever) :
RecipeUseCase.Callback {
From the Dagger User's Guide:
Use #Inject to annotate the constructor that Dagger should use to create instances of a class. When a new instance is requested, Dagger will obtain the required parameters values and invoke this constructor.
If the class with the #Inject-annotated constructor also has a defined scope, then the binding will only affect components with the same scope annotation: An #ActivityScope class wouldn't be accessible from a #Singleton component, for instance. If the class has no scope defined, then the binding could appear on any and all components where it is needed, which is fine: The implementation is always the same, defined by the constructor itself.
#Inject has different meanings based on what it's annotating:
When you annotate a field with #Inject, it indicates that the DI system should set that field based on values from the DI system when it injects that object.
When you annotate a method with #Inject, it indicates that the DI system should call that method with parameters based on values from the DI system when it injects that object.
When you annotate a constructor with #Inject, it indicates that the DI system is allowed to call that constructor in order to create that object (which triggers the field and method injection above). In that sense it does act like a built-in Provider.
In Dagger specifically, #Provides methods take precedence over #Inject constructors (if they both exist for the same class), and unlike in the JSR-330 spec Dagger requires an #Inject-annotated constructor even when no other constructors are present. From the Dagger User's Guide:
If your class has #Inject-annotated fields but no #Inject-annotated constructor, Dagger will inject those fields if requested, but will not create new instances. Add a no-argument constructor with the #Inject annotation to indicate that Dagger may create instances as well.
Classes that lack #Inject annotations cannot be constructed by Dagger.
Finally, to answer your question about why activities and fragments need to inject themselves: Android is allowed to reflectively create Activity/Fragment/View objects without Dagger's help or participation. This means that nothing triggers the field and method injection described above, until you call a members-injection method on the component that instructs Dagger to populate those fields and call those methods. Consequently, you should never see an #Inject-annotated constructor on Application, Activity, Service, Fragment, View, ContentProvider, and BroadcastReceiver subclasses in Android: Android will ignore the #Inject annotation, so you might as well take control over injection yourself, manually or through dagger.android.
I never worked with such a confusing DI-framework like dagger! - However, I try to wrap my head around it.
I have two scopes: ActivityScope and FragmentScope
On some of the samples provided StatisticsFragment.java you see e.g. the fragment annotated with the scope
#ActivityScoped
public class StatisticsFragment extends DaggerFragment implements
StatisticsContract.View {
...
}
Question 1:
Is this just documentation or not? In my app it makes no difference if I annotate the concrete fragment or not.
Question 2: Where in the generated code can I see which scope is used? My fragment injects a Presenter and an AuthProvider. The AuthProvider is annotated with Singleton (in AppModule), the Presenter is defined in UIModule -> LoginModule
looks like this:
UIModule.java:
#Module(includes = AndroidSupportInjectionModule.class)
public abstract class UIModule {
#ActivityScope
#ContributesAndroidInjector(modules = LoginModule.class)
abstract LoginActivity loginActivity();
#ChildFragmentScope
#ContributesAndroidInjector(modules = LoginModule.class)
abstract LoginFragment loginFragment();
#Binds
//#ChildFragmentScope
public abstract LoginContract.View loginView(final LoginFragment fragment);
}
LoginModule.java
#Module
public abstract class LoginModule {
#Provides
//#ChildFragmentScope
static LoginContract.Presenter provideLoginPresenter(final LoginContract.View view, final BaseStore store) {
return new LoginPresenter(view,store);
}
}
LoginFragemt.java
public class LoginFragment extends DaggerFragment {
#Inject
LoginContract.Presenter presenter;
#Inject
Provider<MyAuthClass> myAuthClass;
...
}
presenter is created every time the Fragment gets created, myAuthClass gets created only once and is singleton.
Perfect - but I have no idea HOW this works!!!
DaggerFragment#onAttach must somehow know that Presenter is a "local" singleton and MyAuthClass is a global-singleton ...
Scope is one of two ways you can tell Dagger to always bind the same object, rather than returning a newly-created one on each injection request. (The other way is the manual way: Just return the same object in a #Provides method.)
First, a scope overview: Let's say you have a component, FooComponent, which has a #FooScope annotation. You define a subcomponent, BarComponent, which has a #BarScope annotation. That means that using a single FooComponent instance, you can create as many BarComponent instances as you want.
#FooScoped
#Component(modules = /*...*/)
public interface FooComponent {
BarComponent createBarComponent(/* ... */); // Subcomponent factory method
YourObject1 getYourObject1(); // no scope
YourObject2 getYourObject2(); // FooScoped
}
#BarScoped
#Subcomponent(modules = /*...*/)
public interface BarComponent {
YourObject3 getYourObject3(); // no scope
YourObject4 getYourObject4(); // BarScoped
YourObject5 getYourObject5(); // FooScoped
}
When you call fooComponent.getYourObject1(), YourObject1 is unscoped, so Dagger does its default: create a brand new one. When you call fooComponent.getYourObject2(), though, if you've configured that YourObject2 to be #FooScoped, Dagger will return exactly one instance for the entire lifetime of that FooComponent. Of course, you could create two FooComponent instances, but you'll never see multiple instances of a #FooScoped object from the same #FooScoped component (FooComponent).
Now onto BarComponent: getYourObject3() is unscoped, so it returns a new instance every time; getYourObject4() is #BarScoped, so it returns a new instance for each instance of BarComponent; and getYourObject5() is #FooScoped, so you'll get the same instance along the instance of FooComponent from which the BarComponent was created.
Now to your questions:
Question 1: Is this just documentation or not? In my app it makes no difference if I annotate the concrete fragment or not.
In classes that have an #Inject-annotated constructor like StatisticsFragment does, adding a scope annotation is not simply documentation: Without the scope annotation, any requests to inject a StatisticsFragment will generate a brand new one. If you only expect there to be a single instance of StatisticsFragment per Activity, this may be surprising behavior, but it might be hard to notice the difference.
However, adding an #Inject annotation to a Fragment may be something of a controversial move, because the Android infrastructure is able to create and destroy Fragment instances itself. The object that Android recreates will not the scoped one, and it will have its members reinjected onAttach due to DaggerFragment's superclass behavior. I think a better practice is to drop the #Inject annotation from the constructor and stick with field injection for your Fragment. At that point you can drop the scope, because Dagger will never create your Fragment, so it'll never decide whether to create a new one or return an existing one.
Question 2: Where in the generated code can I see which scope is used? My fragment injects a Presenter and an AuthProvider. The AuthProvider is annotated with Singleton (in AppModule), the Presenter is defined in UIModule -> LoginModule
The generated code and scoping is always generated in the Component; subcomponents will have their implementations generated as an inner class of the Component. For each scoped binding, there will be a place in the initialize method where the Provider (e.g. AuthProvider) is wrapped in an instance of DoubleCheck that manages the double-checked locking for singleton components. If nobody asks Dagger to create an object (like StatisticsFragment), Dagger can determine the lack of component factory methods or injections in the graph, and can avoid adding any code generation for it at all—which might be why you're not seeing any.
I'm considering migrating to Dagger 2 some libraries. This library expose a configurable client, each configuration can be named and later retrieved in a singleton fashion.
Let me show a pseudo-code of how the library works from the user perspective:
// initialization
ClientSDK clientA = new ClientSDK.Builder()
.configuration().attributes().here()
.apiKey("someString") // set api key / credentials
.build();
LibraryAuthenticationManager customAuthManager = new MyCustomAuthenticationManager();
ClientSDK clientB = new ClientSDK.Builder()
.configuration().attributes().here()
.apiKey("someStringElse")
.customAuthManager(customAuthManager) // override some default
.baseApiUrl("https://custom.domain.com/and/path") // override some default setting
.build();
ClientSDK.setSingleton("clientA", clientA);
ClientSDK.setSingleton("clientB", clientB);
And when I need an instance elsewhere:
// usage everywhere else
ClientSDK clientB = ClientSDK.singleton("clientB");
clientB.userManager(); // "singleton" using the configuration of clientB
clientB.subscriptionsManager(); // "singleton" using the configuration of clientB
clientB.currentCachedUser(); // for clientB
clientB.doSomething(); // action on this instance of the ClientSDK
ClientSDK instances are created by the user of the library and the ClientSDK statically keep a map of singletons associated to the name.
(The actual behavior of the SDK is slightly different: the naming is automatic and based on a mandatory configuration parameter.)
It's like I have lot of singleton classes with a single point of entry (the ClientSDK) but since I can have multiple configuration of the ClientSDK each with his own singletons instances this are not really singletons.
If I would try write a library like that with Dagger 2 I would do something like:
class ClientSDK {
#Inject SDKConfiguration configuration;
#Inject LibraryAuthenticationManager authManager;
...
}
The problem is that I need each instance of the ClientSDK to have its own configuration and authManager (and many other services) injected. And they need to be definable (the configuration) and overridable (the actual implementation) from the library user.
Can I do something like this with Dagger 2? How?
I've seen I can create custom Scopes but they are defined at compile time and the library user should be the one defining them.
(the library is an Android Library, but this shouldn't be relevant)
Thanks
It sounds like you should be creating stateful/configurable Module instances and then generating separate Components or Subcomponents for each ClientSDK you build.
public class ClientSDK {
#Inject SDKConfiguration configuration;
#Inject LibraryAuthenticationManager authManager;
// ...
public static class Builder {
// ...
public ClientSDK build() {
return DaggerClientSDKComponent.builder()
.configurationModule(new ConfigurationModule(
apiKey, customAuthManager, baseApiUrl)
.build()
.getClientSdk();
}
}
}
...where your ConfigurationModule is a #Module you write that takes all of those configuration parameters and makes them accessible through properly-qualified #Provides methods, your ClientSDKComponent is a #Component you define that refers to the ConfigurationModule (among others) and defines a #Component.Builder inner interface. The Builder is important because you're telling Dagger it can no longer use its modules statically, or through instances it creates itself: You have to call a constructor or otherwise procure an instance, which the Component can then consume to provide instances.
Dagger won't get into the business of saving your named singletons, but it doesn't need to: you can save them yourself in a static Map, or save the ClientSDKComponent instance as an entry point. For that matter, if you're comfortable letting go of some of the control of ClientSDK, you could even make ClientSDK itself the Component; however, I'd advise against it, because you'll have less control of the static methods you want, and will lose the opportunity to write arbitrary methods or throw exceptions as needed.
You don't have to worry yourself about scopes, unless you want to: Dagger 2 tracks scope lifetime via component instance lifetime, so scopes are very easy to add for clarity but are not strictly necessary if you're comfortable with "unscoped" objects. If you have an object graph of true singleton objects, you can also store that component as a conventional (static final field) singleton and generate your ClientSDKComponent as a subcomponent of that longer-lived component. If it's important to your build dependency graph, you can also phrase it the other way, and have your ClientSDKComponent as a standalone component that depends on another #Component.
The issue that I'm facing is that I want to have two independent scopes that don't really fall into a parent-child hierarchy. In my case, I want two types of scopes:
1) "Feature" based scopes. e.g., when a user enters a feature, a scoped component is created. When the user leaves that feature, that scope is destroyed.
2) "Activity" based scopes (this is for an Android app, sorry about the terminology if you don't use Android). When an activity is created, a scoped component is created. When the activity is destroyed, that scope is destroyed.
Subcomponents nor component dependencies work for what I'm after. This is because the feature could end before the activity is destroyed. Similarly, the activity could end before the feature is finished.
I know that I can just use provision methods instead of member injection methods and hold two separate components, but I want the simplicity of being able to just inject all my dependencies in one go into a single object. Does anyone else have any thoughts on this?
This is the best solution I can think of. I felt the same way about coupling my Activity-related objects to other states in my app and implemented something like this.
Say you have three scopes: Application scope, Activity scope, and some Feature scope (eg like a User login state that has their profile info and such). The Activity and Feature scopes are children of the Application scopes, but Feature and Activity are unrelated siblings.
Do not directly inject the Featured scope objects directly into the Activity. Instead, inject a bridge that contains getters to the feature scoped side.
Example:
public interface UserManager {
#Nullable
User getLoggedInUser();
#Nullable
ShoppingCart getShoppingCart();
}
These methods are nullable because the user may or may not be logged in when the other objects attempt to access them. If they are null, the user is not logged in; the feature is not activated.
The implementation of this freely performs the dependency injection from the Feature scoped side since it does not reference anything from the Activity Scope.
public class UserManagerImpl implements UserManager {
#Inject
ShoppingCart cart;
#Inject
User user;
public UserManagerImpl(MyApplication application){
UserScopeComponent component = application.getUserComponent();
if(component != null) {
//only attempt injection if the component exists (user is logged in)
component.inject(this);
}
}
//put simple getters here. They will return null objects if the component didnt inject anything.
}
The ApplicationModule Provides this object. It is not an Application scope or a singleton; you want to initialize it again every time it is injected in case the login state has changed.
#Provides
//No Scope
UserManager provideUserManager(){
return new UserManagerImpl(context);
}
From then on you can inject the UserManager from anywhere in your app, call its getter, check if the output is not null, and away you go. Your Feature state is no longer coupled to the Activity state and you can be free.