In an Android project, there is a facade implemented as the singleton. I thought it is an better idea converting it to DI with HILT SingletonComponent.
#Module
#InstallIn(SingletonComponent::class)
object MyManagerModule {
#Provides
fun provide(#ApplicationContext context: Context): MyManager {
return MyManager(context)
}
}
class MyManager #Inject constructor(#ApplicationContext private val ctx: Context){
//
}
From a few callers, I get the instance of the above MyManager using HILT field injection, i.e.,
#AndroidEntryPoint
class MyCallerFragment : Fragment() {
#Inject lateinit var myManager: MyManager
// ...
In the debugger, I observed the DI instances are actually NOT the same instance (assuming these fragments are in the same activity lifecycle). I think I must have misunderstand the Hilt DI :-( and would love to hear any explanation if you see my blindspots.
TL;DR
You need to use the annotation #Singleton. This will tell Hilt to use the same instance of MyManager throughout your app.
Scope of Bindings
According to the documentation:
By default, all bindings in Hilt are unscoped. This means that each time your app requests the binding, Hilt creates a new instance of the needed type.
and
However, Hilt also allows a binding to be scoped to a particular component. Hilt only creates a scoped binding once per instance of the component that the binding is scoped to, and all requests for that binding share the same instance.
The #Singleton annotation scopes your Hilt binding to the application component. (including all children, which are all components) So Hilt will inject the same instance of your object throughout your entire app.
There is an example in this guide by Google.
The #InstallIn annotation
The annotation #InstallIn() tells Hilt in which component MyManager objects will be injected.
In your case of #InstallIn(SingletonComponent::class), Hilt will make MyManager available for injection in the application component and all children of this component, which doesn't mean that Hilt will supply the same instance though. Since any default component is a child of the application component, MyManager is currently accessible for injection in any component. (according to the documentation)
I had kinda the same question, Scope vs Component in Hilt, and this is what I got:
When you annotate a class with #InstalIn, actually you are defining the lifetime of this dependencies' module. For example, if you use SingletonComponent all dependencies from this class(Module) are stay as long as the application is up.
And for Scope, when you provide a dependency in a module, each time that module is called, a new instance is created. this is why you observed in debugger generated instances are not the same. by using Scopes like #Singleton you are changing the way of instantiation of dagger-hilt on called dependencies.
so generally speaking, Component is for instance lifetime and Scope is for the way of dependency instantiation on each call.
don't miss this article:
https://medium.com/digigeek/hilt-components-and-scopes-in-android-b96546cb07df
Related
Without using FragmentFactory, scoping dependencies within Fragment is straigth forward. Just create a Subcomponent for your fragment and just create the Fragment's Subcomponent in onAttach().
But when using FragmentFactory you no longer inject dependencies via the subcomponent but instead pass then in Fragment's constructor.
I was wondering if I could still declare a dependency which should only last within the fragment's lifecycle using Dagger. I currently could not think of a way to achieve this.
So instead of binding my dependencies to a certain scope, I just declare the dependency with any scope or just use #Reusable on them.
Also, since fragments are created through FragmentFactory, the created Fragments doesn't exist in the DI graph.
How can we properly scope dependencies to fragment and also be able to add the fragment in the DI graph when using FragmentFactory?
You can accomplish this by making a Subcomponent responsible for creating your Fragment. The Fragment should have the same scope as this Subcomponent.
#Subcomponent(modules = [/* ... */])
#FragmentScope
interface FooSubcomponent {
fun fooFragment(): FooFragment
#Subcomponent.Factory
interface Factory {
fun create(): FooSubcomponent
}
}
After taking care of any cyclic dependency issues, this Subcomponent acts the same as if you had explicitly created a subcomponent in onAttach() using #BindsInstance, except that you can (and must) now use constructor injection on FooFragment.
In order to provide FooFragment in your main component (or parent subcomponent), you will need to install this Subcomponent into a module.
#Module(subcomponents = [FooSubcomponent::class])
object MyModule {
#Provides
#IntoMap
#FragmentKey(FooFragment::class)
fun provideFooFragment(factory: FooSubcomponent.Factory): Fragment {
return factory.create().fooFragment()
}
}
Some caveats:
The scenario you describe (FooFragment depends on some class which depends on FooFragment) is the definition of a cyclic dependency. You will need to inject a Lazy or Provider at some point in this cycle, or Dagger will throw an error at compile time.
If you split this up into two steps, first providing FooFragment and then binding it into your map, the subcomponent will see the #Provides method from its parent component. This is likely to cause a StackOverflowError if you aren't careful.
I'm looking for a quick confirmation about Dagger 2 scopes in Android.
In many resources online you will find that #ActivityScope and #FragmentScope are added to components that provide bindings for activities and fragments.
I would like to have some confirmation that this implies that there will be 1 instance for all activities / all fragments respectively.
That is, if, say, two activities use the same component for receiving dependencies from the same component annotated with scope 'Activity', both activities will receive the same instance (like singleton annotation would work).
So in that case having #ActivityScope and #FragmentScope annotations would only be useful to segregate between dependency lifetimes between activities versus fragments.
So if I would need a dependency object for which I need a separate instance in two activities, I should scope them explicitly (e.g. #LoginActivityScope).
Could you confirm that this assumption is correct?
Edit:
Reading the docs about subcomponents, it confuses me a bit:
No subcomponent may be associated with the same scope as any ancestor
component, although two subcomponents that are not mutually reachable
can be associated with the same scope because there is no ambiguity
about where to store the scoped objects. (The two subcomponents
effectively have different scope instances even if they use the same
scope annotation.)
This would seem to assume that if you have multiple components using the same annotation, it does create a separate instance when the same scope annotation is used for different components.
I find it a bit unclear as to what a scope instance refers to. This actually refers to the binding?
Does this only apply to subcomponents?
Some clarification about scope vs dependency instances (bindings) would be very helpful.
A scoped component will create a scoped object the first time it is used, then it will hold on to it. If you create the same component a second time it will also create the scoped object the first time it gets used. Components are just objects, they don't hold any global (static) state, so if you recreate the component, you recreate everything along with it.
val component = DaggerScopedComponent.create()
component.getScopedObject() === component.getScopedObject() // always the same object!
// never the same object! two different components, albeit same scope
DaggerScopedComponent.create().getScopedObject() != DaggerScopedComponent.create().getScopedObject()
Dagger generates code, so I would invite you to create a simple example and have a look at the code. e.g. the sample above should be very easy to read
#Singleton class Foo #Inject constructor()
#Singleton #Component interface ScopedComponent {
fun getScopedObject() : Foo
}
If you have a scoped component that lives longer than its subscopes then you have to keep a reference to this component and reuse it. The usual practice is to hold a reference to the component in the object whose lifecycle it shares (Application, Activity, Fragment) if needed.
Let's say we add a subcomponent to the example above
#Singleton class Foo #Inject constructor()
#Singleton #Component interface ScopedComponent {
fun getScopedObject() : Foo
fun subComponent() : SubComponent
}
#Other #Subcomponent interface SubComponent {
fun getScopedObject() : Foo
}
#Scope
#MustBeDocumented
annotation class Other
As long as we use the same #Singleton component we will always get the same #Singleton scoped objects.
// Subcomponents will have the same object as the parent component
component.subComponent().getScopedObject() === component.getScopedObject()
// as well as different Subcomponents
component.subComponent().getScopedObject() === component.subComponent().getScopedObject()
Now on to your questions...
I would like to have some confirmation that this implies that there will be 1 instance for all activities / all fragments respectively.
That is, if, say, two activities use the same component for receiving dependencies from the same component annotated with scope 'Activity', both activities will receive the same instance (like singleton annotation would work).
As shown above, any scoped object provided from the same scoped component will be the same no matter which subcomponent. If you create two #ActivityScope MyActivityComponent then everything scoped #ActivityScoped will be created once per component.
If you want objects to be shared between your Activities' components you have to use a higher scope and keep the reference to the created component.
So in that case having #ActivityScope and #FragmentScope annotations would only be useful to segregate between dependency lifetimes between activities versus fragments.
No, because you can have a #ActivityScope FooActivityComponent and a ActivityScope BarActivityComponent and they would never share a #ActivityScope class FooBar object, which will be created once for every #ActivityScope scoped component.
So if I would need a dependency object for which I need a separate instance in two activities, I should scope them explicitly (e.g. #LoginActivityScope).
#ActivityScope FooActivityComponent and #ActivityScope LoginActivityComponent will never share any #ActivityScope scoped objects. You can use the same scope here. You can also create a different scope if you like to do so, but it would make no difference here.
This would seem to assume that if you have multiple components using the same annotation, it does create a separate instance when the same scope annotation is used for different components.
Yep
I find it a bit unclear as to what a scope instance refers to. This actually refers to the binding? Does this only apply to subcomponents?
You can't have a hierarchy of components like Singleton > ActivityScope > ActivityScope since those duplicated scopes would make it impossible to know whether a #ActivityScope scoped object was part of the first or second one.
You can have two different components of the same scope, both subcomponents of the same parent (they can't "reach" each other), and any #ActivityScope scoped object would be part of the latter #ActivityScope scoped component. You'd have one scoped object per component (as shown in the example above) and you could have two component instances or more.
Singleton > ActivityScope FooComponent
Singleton > ActivityScope BarComponent
I recommend you forget about Android for a bit and just play around with Dagger and the generated code, like with the code shown on top. This is IMHO the quickest way to figure out how things work, once the "magic" is gone and you see that it's just a POJO with a few variables in it.
I'm starting to think in mvp, dagger should not be used in the presenter. The usual way to construct dagger is using a global component and have subcomponents for scoping the graph. This global component will take an applicationContext as a parameter on creating the appmodule.java class usually. Having the application context given makes life easier.
That's all fine but if I use a module from the global component or even a subcomponent, the context should be passed in. So that means if I inject the presenter with dagger it will be tied to the applicationContext. THIS MAKES IT DIFFICULT TO TEST as junit. Android code should not be in presenter.
So I'm asking is the best practice to just use dagger in activities fragments broadcast receivers and services only? Speaking in terms of mvp architecture that is. The other solution could be to design another dagger component but not a subcomponent that is not related to appcomponent or an applicationContext. What is the common practice?
UPDATE:
LETS look at a real example:
usually we'd create a appComponent that's close in application override like this:
public class MyApplication extends Application {
private AppComponent appComponent;
#Override
public void onCreate() {
super.onCreate();
appComponent = initDagger(this);
}
public AppComponent getAppComponent() {
return appComponent;
}
protected AppComponent initDagger(PomeloApplication application) {
return DaggerAppComponent.builder()
.appModule(new AppModule(application))
.build();
}}
and then for example in the presenter we'd do this in the constructor:
public WelcomePresenter(Context context) {
super(context);
presenterComponent = ((MyApplication) context).getAppComponent();
presenterComponent.inject(this);
}
so because I wanted the application Context for dagger providers I ended up forcing callers to depend on a context Object. Basically, you cant get the AppComponent modules until you get a context so you can cast it as "MyApplication" and inject from the component? So i was thinking why is this dagger pattern making me force the presenter to have a context?
Even if we used a subComponent it would still depend on the AppComponent and then in our test case we'd have to deal with an application context being created to get dagger to inject.
So I think there should be a rule - in MVP prefer manual constructor injection over dagger injection
Because class shouldn’t have knowledge about how it is injected. Something being injected should not care about how it is injected.
dagger.android outlines the problem i am talking about as per the accepted answer.
I also agree with the statement "Not having android related code in your presenter is always a good idea, and dagger does not interfere with that."
First, what I want to draw attention that starting from dagger version 2.10 you can use dagger.android package which simplifies injection in the activities and fragments, so you don't need to use MyApplication casting across the app.
Second, why are you not injecting WelcomePresenter into activity or fragment but vice versa?
Dependency injection is useful for Android components (activities, etc.) primarily because these classes must have a no-arg constructor so the framework can construct instances of them. Any class that isn't required to have a no-arg constructor should simply receive all its dependencies in its constructor.
Edit: A side note about how you're getting a reference to the AppComponent.
Application is-a Context, and the Context returned by Context#getApplicationContext() is usually the Application instance. But this is not necessarily the case. The one place I know of where it may not be the case is in an emulator. So this cast can fail:
presenterComponent = ((MyApplication) context).getAppComponent();
Mutable static fields are evil in general, and even more so on Android. But IMO, the one place you can get away with it is in your Application class. There is only one instance of Application, and no other component will ever exist before Application#onCreate() is called. So it's acceptable to make your AppComponent a static field of MyApplication, initialize it in onCreate(), and provide a static getter, as long as you only call the getter from the lifecycle methods of other components.
We are creating a dagger 2 dependency graph
SessionComponent (Session scope) --dependson---> Appcomponent (Singleton scope) ---dependson---> UserMangerComponent (unscoped... suppose to be singleton)
However, when I inject userManager (a dependency provided by UserManagerComponent), It is not being maintained as singleton. Every injection is creating a new UserManager. Please help ...
I cannot set singleton scope to the dependency.
Your proposed structure is incompatible with how Dagger manages scope. Only one component in your app should ever be #Singleton because each binding within a component that is not exposed via a component interface might be #Singleton, but entirely encapsulated in the component implementation. Thus, each component would hold its own instance and you'd end up with 2 instances rather than one.
Either merge the two components into one #Singleton component, create a new scope for your user management, or implement the instance management for your user manager by hand.
I have some misunderstandings of the way that dagger works:
There are only two ways to satisfy dependency: whether the #Provide method returns the instance, or the class should have #Singleton annotation, is that right? Must the class constructor have #Inject annotation in the latter case?
As I see, the ObjectGraph generates all the injection stuff. And it's said, that its inject(T instance) should be called to inject fields. However, I can just annotate my field with #Inject and it goes (field's class is #Singletone). ObjectGraph is not needed to satisfy such a dependency, right?
What about injects{} in #Module, what specifically does it give? Plz, provide an example of benefit when you keep all the injectable classes list.
Yes, there are two ways: #Provide methods in modules and #Singleton classes with #Inject at constructor.
Must the class constructor have #Inject annotation in the latter case?
Yes, otherwise object wouldn't be created by Dagger.
I don't think #Singleton with field injection could work. Because #Singleton at class with constructor injection means Dagger is responsible to keep one instance of this class. And it can create this class using constructor injection if all dependencies are satisfied. However, #Singleton with field injection seems misuse to me, cause keeping single instance of this class is user's responsibility now. Dagger can't instantiate this object itself.
Are you sure this configuration compiles and runs? And if it is check #Inject fields, they should be null by my understanding.
injects={} in #Module returns set of classes passed to ObjectGraph.inject(T class) or ObjectGraph.get(Class<T> class). Referring documentation:
It is an error to call ObjectGraph.get(java.lang.Class) or ObjectGraph.inject(T) with a type that isn't listed in the injects set for any of the object graph's modules. Making such a call will trigger an IllegalArgumentException at runtime.
This set helps Dagger to perform static analysis to detects errors and unsatisfied dependencies.
You can find some examples in this thread.