Dagger: @Qualifier causes: [Dagger/MissingBinding] androidx.lifecycle.ViewModelProvider.Factory cannot be provided without an @Provides-annotated method.

Created on 4 Jun 2019  Â·  2Comments  Â·  Source: google/dagger

Setup:

  • Android Plugin: 3.4.1
  • Dagger: 2.23.1
  • Kotlin: 1.3.31
  • Gradle: 5.1.1

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.

Dagger2IssueSample.zip

Most helpful comment

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:

  1. param
  2. property
  3. field

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

All 2 comments

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:

  1. param
  2. property
  3. field

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

Was this page helpful?
0 / 5 - 0 ratings

Related issues

matpag picture matpag  Â·  3Comments

blackberry2016 picture blackberry2016  Â·  3Comments

mskx42 picture mskx42  Â·  3Comments

JakeWharton picture JakeWharton  Â·  3Comments

6bangs picture 6bangs  Â·  3Comments