Setup:
Error:
/Dagger2Sample/app/build/tmp/kapt3/stubs/debug/com/catalinjurjiu/dagger2sample/di/components/AppComponent.java:8: error: [Dagger/MissingBinding] androidx.lifecycle.ViewModelProvider.Factory cannot be provided without an @Provides-annotated method.
public abstract class AppComponent {
^
androidx.lifecycle.ViewModelProvider.Factory is injected at
com.catalinjurjiu.dagger2sample.MainActivity.factory
com.catalinjurjiu.dagger2sample.MainActivity is injected at
dagger.android.AndroidInjector.inject(T) [com.catalinjurjiu.dagger2sample.di.components.AppComponent → com.catalinjurjiu.dagger2sample.di.modules.ActivityBindingModule_InjectMainActivity.MainActivitySubcomponent]
The issue only appears when attempting to use a @Qualifier (@MyViewModelsFactory) for my ViewModelProvider.Factory. If the @MyViewModelsFactory qualifier is removed from the @Binds method in the module, and from the Activity where I inject my ViewModelProvider.Factory, then the project compiles correctly.
Qualifier declared as following (Kotlin):
@Qualifier
@MustBeDocumented
@Retention(AnnotationRetention.RUNTIME)
annotation class MyViewModelsFactory
Is this a bug or am I missing something?
If it is a bug I thought it might be related to https://github.com/google/dagger/issues/1478, but the fixes suggested there didn't help: it still reproduces even after downgrading Kotlin to 1.3.20 & after rewriting the @MapKey in Java.
Sample project which reproduces the issue attached. Commenting line 13 from ViewModelBindingModule & line 16 from MainActivity will make the error disappear & the sample will compile without errors.
Dagger annotations on field injections in Kotlin need to have the proper use-site target declared, otherwise the annotation will be applied to the wrong generated JVM code.
When you write:
@MyViewModelsFactory
lateinit var factory: ViewModelProvider.Factory
By default, Kotlin will generate 3 pieces of code in Java:
1 >> public ViewModelProvider.Factory factory;
2 >> public ViewModelProvider.Factory getFactory() { .. }
3 >> public void setFactory(ViewModelProvider.Factory factory) { .. }
Now, which piece of code does Kotlin apply the annotation to?
By default, without a specified use-site target, Kotlin will add the annotation only to the first match in the following list:
getFactory() and setFactory(..) are considered the generated property, so they receive the annotation MyViewModelsFactory.
Recall that Dagger _field_ injection requires the annotation to be placed on the _field_.
You can solve this by explicitly telling Kotlin to apply the annotation to fields as well, via the @Target annotation.
@Qualifier
@MustBeDocumented
@Retention(AnnotationRetention.RUNTIME)
@Target(FUNCTION, VALUE_PARAMETER, FIELD)
annotation class MyViewModelsFactory
You can also solve this by telling the annotation to be applied to the field explicitly.
@field:MyViewModelsFactory
lateinit var factory: ViewModelProvider.Factory
reference: https://kotlinlang.org/docs/reference/annotations.html#annotation-use-site-targets
Closing since this is fixed in https://github.com/google/dagger/releases/tag/dagger-2.25.2.
Most helpful comment
Dagger
annotationsonfieldinjections in Kotlin need to have the proper use-site target declared, otherwise the annotation will be applied to the wrong generated JVM code.When you write:
By default, Kotlin will generate 3 pieces of code in Java:
Now, which piece of code does Kotlin apply the annotation to?
By default, without a specified use-site target, Kotlin will add the annotation only to the first match in the following list:
getFactory()andsetFactory(..)are considered the generated property, so they receive the annotationMyViewModelsFactory.Recall that Dagger _field_ injection requires the
annotationto be placed on the _field_.You can solve this by explicitly telling Kotlin to apply the annotation to fields as well, via the
@Targetannotation.You can also solve this by telling the annotation to be applied to the field explicitly.
reference: https://kotlinlang.org/docs/reference/annotations.html#annotation-use-site-targets