Whats the difference between #Inject and #Provide ?
although both are used for providing dependencies then whats the difference ?
This is covered very well in documentation, #Inject and #Provides are two different ways of introducing dependencies in the dependency graph. they are suited for different use cases
#Inject
Easy to use, simply add #Inject on constructor or property and you are done
It can be used to inject types as well as type properties
In a subjective way it may seem clearer than #Provides to some people
#Provides
If you don't have access to source code of the type that you want to inject then you can't mark its constructor with #Inject
In some situations you may want to configure an object before you introduce it in dependency graph, this is not an option with #Inject
Sometimes you want to introduce an Interface as a dependency, for this you can create a method annotated with #Provides which returns Inteface type
Following are the examples of above three points for #Provides
If you can't access source code of a type
// You can't mark constructor of String with #Inject but you can use #Provides
#Provides
fun provideString(): String {
return "Hello World"
}
Configure an object before introducing in the dependency graph
#Provides
fun provideCar(): Car {
val car = Car()
// Do some configuration before introducing it in graph, you can't do this with #Inject
car.setMaxSpeed(100)
car.fillFuel()
return car
}
Inject interface types in dependency graph
interface Logger { fun log() }
class DiscLogger : Logger{ override fun log() { } }
class MemoryLogger : Logger { override fun log() { } }
#Provides
fun provideLogger(): Logger {
val logger = DiscLogger() \\ or MemoryLogger()
return logger
}
#Inject:- It is used to inject dependencies in class.
#provides:- Required to annotated the method with #provide where actual instance is created.
This has covered in Dagger-2 documentation clearly.
What #Inject can Do:
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 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.
Similarly dagger can create for methods also.
Classes that lack #Inject annotations cannot be constructed by
Dagger.
What #Inject can't Do: and can be used #Provides annotation
Interfaces can’t be constructed.
Third-party classes can’t be annotated.
Configurable objects must be configured!
Related
I started to migrate Dagger application to Hilt, first I'm converting AppComponent to Hilt auto-generated ApplicationComponent. Therefore I've added #InstallIn(ApplicationComponent::class) annotation to each module related to this component.
Now I get the following error:
error: [Hilt] All modules must be static and use static provision
methods or have a visible, no-arg constructor.
It points to this module:
#InstallIn(ApplicationComponent::class)
#Module
class AccountModule(private val versionName: String) {
#Provides
#Singleton
fun provideComparableVersion(): ComparableVersion {
return ComparableVersion(versionName)
}
}
Previously in Dagger, it was possible to pass arguments in the constructor. Looks like Hilt doesn't allow this.
How can I pass arguments to Hilt module?
#InstallIn(ApplicationComponent::class)
#Module
class AccountModule {
#Provides
#Singleton
fun provideComparableVersion(application: Application): ComparableVersion {
return ComparableVersion((application as MyApplication).versionName)
}
}
If you don't want to see MyApplication, then you can use an interface.
Unfortunately for now Dagger Hilt is design using monolithic component, where there's only one Application Component and one Activity Component auto generated by it. Refer to https://dagger.dev/hilt/monolithic.html
Hence the modules for it must be static and use static provision methods or have a visible, no-arg constructor.
If you put an argument to the module, it will error out stating
[Hilt] All modules must be static and use static provision methods or have a visible, no-arg constructor.
From my understanding, you'll trying to get the BuildInfo version number, perhaps the easiest way is to use the provided BuildInfo.VERSION_NAME as below.
#InstallIn(ApplicationComponent::class)
#Module
class AccountModule() {
#Provides
#Singleton
fun provideComparableVersion(): ComparableVersion {
return ComparableVersion(BuildInfo.VERSION_NAME)
}
}
If you like to set it yourselves instead of relying on BuildInfo.VERSION_NAME, you can define static const variable that exist differently across flavour.
I have a class that is dagger injected via the constructor. However I now need to add an argument to this constructor that is not provided via injection and is instead a run time argument.
In my previous job, we rolled our own DI so I am not up to speed with all the "magic" annotations that dagger offers yet. I figured it should be simple to add an argument to a constructor and still have dagger inject the remaining values (as it was very simple to do with the aforementioned "roll your own DI" solution I have implemented before).
However, it looks like this is not possible with dagger (i.e. assisted injection). So I have been reading on how to solve this issue but have become completely stumped.
Here is the class that I am currently (successfully) injecting ServiceA into:
class Foo #Inject constructor(private val serviceA: ServiceA) {
...
}
What I would like to do is add another argument to this constructor that will be provided at run time. In this case a simple flag to determine some behaviour in the class. And, given that Dagger doesn't support assisted injection, I need to remove injection from Foo and instead create a FooFactory to handle creation of Foo objects. This factory will have ServiceA injected into its constructor and will provide a method to construct an instance of Foo taking the boolean. I will then end up with a Foo class that looks like:
class Foo(private val serviceA: ServiceA, myNewArgument: Boolean) {
...
}
And a FooFactory that looks like:
#Singleton
class FooFactory #Inject constructor(private val serviceA: ServiceA) {
fun createFoo(myNewArgument: Boolean) {
return Foo(serviceA, myNewArgument)
}
}
And, although this is a complete mess to just get an extra constructor arg, it does the job.
The problem I am facing, is that my Foo class is actually an AndroidViewModel, and will need to be constructed through the ViewModelProvider.Factory contract, which has a create method which is invoked by the SDK to create the view model. You override this method to create an instance of the view model but the method has no parameters, so there is no way to propagate the flag into the view model through this method.
So the only way for me to get the flag propagated to the view models constructor is by having the factory itself take the flag as an argument to it's constructor, which, because dagger does not support assisted injection, is not possible.
So, instead I am planning to make dagger manually inject my dependencies into the FooFactory at initialization time. This is where I am stuck, I cannot figure out how on earth to get dagger to manually inject dependencies into my class.
My FooFactory now looks like:
class FooFactory(private val myNewArgument: Boolean) : ViewModelProvider.Factory {
init {
// I need to trigger injection here... how though???
}
#Inject
lateinit var serviceA: ServiceA
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return Foo(
serviceA,
myNewArgument
) as T
}
}
So, somehow in the init block of the factory, I need to ask dagger to inject the fields annotated with #Inject. But I have no idea how to do this, I have tried following the answers at the following questions and tutorials:
https://proandroiddev.com/from-dagger-components-to-manual-dependency-injection-110015abe6e0
Dagger 2 - injecting non Android classes
Dagger 2 injection in non Activity Java class
None of these seem to work for my use case and I'm starting to lose it. Could anyone point me in the right direction? Is this dagger framework massively over engineered/complicated for not much benefit (this is the conclusion I am coming to at this point, all I want to do is achieve DI for testing purposes, I don't want to have to write factories so I can add an extra argument to a constructor)...
im creating a highly modular application, i have a lot of clases that need to be injected, all of them are childs (not direct childs) of the same class, none of them have constructor parameters.
I want to avoid having to create a "#Provides" method for each one of them in my module.
Is there a way to tell dagger to automatically provide all the classes that implement a base interface? Or is it possible to do it myself using reflection?
Im using dagger-android with kotlin
Update: Ill post some code to illustrate
In one of the modules i have this interface
interface ExampleClass: BaseExample {
fun doSomething()
}
}
Then in the main app i implement it
class ExampleClassImpl #Inject constructor() : ExampleClass {
override fun doSomething(){
}
}
The class where i need it is a Viewmodel created with dagger so inject works on the constructor.
class ExampleViewModel #Inject constructor(val exmpl :ExampleClass) : BaseViewModel {
}
I want to inject that ExampleClassImpl, to do that i need to create a #module with a method annotated with #Provides or #Bind and return that class.
Without the provider i get an error at compile time:
error: [Dagger/MissingBinding] com.myapp.ExampleClassImpl cannot be provided without an #Provides-annotated method.
You want to inject ExampleClass, but Dagger only knows about ExampleClassImpl. How would Dagger know that you want that specific subclass?
Moreover, you say you have many subclasses that you want to inject. How would Dagger know which one to provide to a constructor expecting the base class?
If you want ExampleViewModel to get an instance of ExampleClassImpl then you can simply change the declaration to:
class ExampleViewModel #Inject constructor(val exmpl :ExampleClassImpl)
Doing so you lose the ability to swap the constructor argument with a different implementation of ExampleClass.
The alternative is to have one #Named #Provides method per subclass. So something like:
// In your module
#Provides
#Named("impl1")
fun provideExampleClassImpl1(impl: ExampleClassImpl): ExampleClass = impl
// When using the named dependency
class ExampleViewModel #Inject constructor(#Named("impl1") val exmpl :ExampleClass)
I am new to Dagger 2 and trying to implement it in Kotlin. Here i am trying to inject my repository object into viewmodel. I am successfully able to inject it this way
public class LoginViewModel #Inject constructor(var mApplication: Application, var repository: LoginRepository) :
ViewModel() {
This is how my repository looks like
class LoginRepository #Inject constructor(val retrofit: APICallInterface) {
This is how my module looks like
#Module
class BaseModule {
#Provides
fun getRetrofit(): APICallInterface {
return Retrofit.Builder()
.baseUrl("https://samples.openweathermap.org/data/2.5/")
.addConverterFactory(GsonConverterFactory.create())
.build().create(APICallInterface::class.java)
}
What i am unable to understand is how Dagger 2 is able to provide an object for repository as i have not mentioned it in any module with #Provides annotation.
I have tried following many blogs and few stckoverflow questions available here but none of them is solving my doubt.
Any help/explanation will be appreciated.
What i am unable to understand is how Dagger 2 is able to provide an object for repository as i have not mentioned it in any module with #Provides annotation.
You're using constructor injection by annotating the constructor with #Inject:
[#Inject] Identifies injectable constructors, methods, and fields.
So, by adding the annotation, Dagger becomes aware of the constructor and knows how to create the object when needed.
class LoginRepository #Inject constructor(..)
If your constructor wouldn't have the annotation on it then you'd need a #Provides annotated method in a module so that Dagger can access the dependency, but you should use #Provides annotated methods primarily for objects that need additional setup and/or initialization.
I provide ImagesRepo using RepoModule, ImgesReo depends on RxApiController and SharePreferenceHelper and i am providing these dependencies in RepoModule itself, these dependencies comes from AppModule.
#Module(includes = AppModule.class)
public class RepoModule {
#Provides
#Inject
public ImagesRepo providesImagesRepo(RxApiController rxApiController, SharePreferenceHelper sharePreferenceHelper) {
return new ImagesRepo(rxApiController, sharePreferenceHelper);
}
}
When i try to inject ImagesRepo like this
#Inject
public ImagesRepo imagesRepo;
public MyActivityViewmodelImpl() {
MyApplication.getRepoComponent().inject(this);
}
It shows error if i remove #Inject from constructor of ImagesRepo, I think that i am providing RxApiController and SharePreferenceHelper from RepoModule
#Inject
public ImagesRepo(RxApiController rxApiController, SharePreferenceHelper sharePreferenceHelper) {
super(rxApiController, sharePreferenceHelper);
}
Question is why i am suppose to add #Inject at ImagesRepo constructor, if i am providing dependencies for ImagesRepo in RepoModule itself
To avoid errors and confusion you should make sure to understand what each annotation does. A #Provides method does not need an #Inject annotation. Here, you could even use constructor injection alone, and you wouldn't need the #Provides method (or the module) at all, reducing the amount of boilerplate needed.
As to your specific error, I would guess that you either don't add RepoModule to your component, or that you try to inject your class with the wrong component (that does not have access to RepoModule).
Adding #Inject on the constructor of ImagesRepo will mark it for constructor injection so that Dagger can and will create it for you. There is no need for the module (that you didn't add / component can't access), which is why it will "work" when you do so.
To avoid confusion and errors either use a #Provides method from a module or use constructor injection—preferrably constructor injection which will eliminate the boilerplate, which is one of the reasons why you'd use Dagger in the first place.