How can I create a singleton that gets injected with an application context. Just annotating with #Singleton and then using #Inject on the constructor ends up generating an UnscopedProvider class that will not compile. How can I make an ApplicationScoped provider... or is there another mechanism to accomplish this?
The solution I ended on here is to create an init(Application app) {} method on the Singleton, then inject the Singleton into my #Application annotated class and call the init method using the Application that's injected into that class. I think this is the only way to do this currently.
Related
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
In regards to #ApplicationContext and #ActivityContext in Hilt, I understand that they help to resolve ambiguity when a Context is being requested… but why not just request Application or Activity directly?
It depends on what you need, when you just need a context(call methods in the context class), then inject the context. If you really need an Activity, you can inject it to your class.
But when you inject some class, it means your class depends on it and they are coupled. That's a bad practise in software design. When you inject an activity into your class, that means your class only works with that activity. If you inject an context, your class can work with any subclasses that implements the Context class
While running an Espresso test on Android, is there any way to inject dependencies within a custom TestRule?
why would you inject in Testcases? You should use DI when you need to pass instances in constructors... using this approach lets you mock those instances in testcases...
#Inject //Constructor getting injected instance
public classToBeTested(NewInstance instancenew){......}
If you test this class you can provide any mock-object to this class... This should be the way using DI...
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.
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.