Search code examples
androidkotlindagger-2android-architecture-componentsandroid-viewmodel

Dagger MissingBinding error when injecting ViewModels


I'm new to dagger and want to use it for injecting ViewModels (along with other objects such as repositories). When I try to compile the app this error is shown:

e: C:\Project\...\app\build\tmp\kapt3\stubs\debug\com\myapp\di\AppComponent.java:16: error: [Dagger/MissingBinding] java.util.Map<java.lang.Class<? extends androidx.lifecycle.ViewModel>,javax.inject.Provider<androidx.lifecycle.ViewModel>> cannot be provided without an @Provides-annotated method.

public abstract interface AppComponent {
                ^
      java.util.Map<java.lang.Class<? extends androidx.lifecycle.ViewModel>,javax.inject.Provider<androidx.lifecycle.ViewModel>> is injected at
          com.myapp.di.ViewModelFactory(viewModels)
      com.myapp.di.ViewModelFactory is injected at
          com.myapp.di.ViewModelModule.bindViewModelFactory(factory)
      androidx.lifecycle.ViewModelProvider.Factory is injected at
          com.myapp.ui.activity.MainActivity.viewModelFactory
      com.myapp.ui.activity.MainActivity is injected at
          dagger.android.AndroidInjector.inject(T) [com.myapp.di.AppComponent ? com.myapp.di.ActivityModule_ContributeMainActivity.MainActivitySubcomponent]
  The following other entry points also depend on it:
      dagger.android.AndroidInjector.inject(T) [com.myapp.di.AppComponent ? com.myapp.di.ActivityModule_ContributeItemsFrament.ItemsFragmentSubcomponent]

Maybe there is one silly mistake that prevents compilation.

Dagger component:

@Singleton
@Component(modules = [AndroidSupportInjectionModule::class, AppModule::class, DatabaseModule::class, ViewModelModule::class, ActivityModule::class])
interface AppComponent {

    @Component.Builder
    interface Builder {
        @BindsInstance
        fun application(application: MyCustomApplication): Builder

        fun build(): AppComponent
    }

    fun inject(app: MyCustomApplication)
}

AppModule:

@Module
class AppModule {

    @Inject
    lateinit var app: Application

    @Provides
    @Singleton
    fun provideAppContext(): Context = app.applicationContext
}

ActivityModule:

@Module
abstract class ActivityModule {

    @ContributesAndroidInjector
    abstract fun bindMainActivity(): MainActivity

    @ContributesAndroidInjector
    abstract fun contributeMainFrament(): MainFragment
}

ViewModelModule:

@Module
abstract class ViewModelModule {

    @Binds
    abstract fun bindViewModelFactory(factory: ViewModelFactory): ViewModelProvider.Factory

    @Binds
    @IntoMap
    @ViewModelKey(MainViewModel::class)
    abstract fun mainViewModel(mainViewModel: MainViewModel): ViewModel
}

ViewModelFactory:

@Singleton
class ViewModelFactory
@Inject constructor(private val viewModels: MutableMap<Class<out ViewModel>, Provider<ViewModel>>)
: ViewModelProvider.Factory {

    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        val creator = viewModels[modelClass]
                ?: viewModels.asIterable().firstOrNull { modelClass.isAssignableFrom(it.key) }?.value
                ?: throw IllegalArgumentException("unknown model class $modelClass")
        return try {
            creator.get() as T
        } catch (e: Exception) {
            throw RuntimeException(e)
        }
    }
}

The application class:

class MyCustomApplication : Application(), HasActivityInjector, HasSupportFragmentInjector {

    @Inject lateinit var activityInjector: DispatchingAndroidInjector<Activity>

    override fun onCreate() {
        super.onCreate()

        DaggerMainComponent
                .builder()
                .application(this)
                .build()
                .inject(this)
    }

    override fun activityInjector() = activityInjector
}

MainViewModel (note that it extends from AndroidViewModel and requires an application context):

class MainViewModel @Inject constructor(app: Application, private val repository: MainRepository)
    : AndroidViewModel(app) {

MainActivity:

class MainActivity : BaseActivity() {

    @Inject
    internal lateinit var viewModelFactory: ViewModelProvider.Factory
    private lateinit var viewModel: MainViewModel

    override fun onCreate(savedState: Bundle?) {
        AndroidInjection.inject(this)
        super.onCreate(savedState)
        viewModel = ViewModelProviders.of(this,viewModelFactory).get(MainViewModel::class.java)

Solution

  • I faced same issue combining Android X and Kotlin version 1.3.0, Dagger won't be able to create MultiBindings. To solve the problem I just followed next steps:

    a) Update Kotlin version to 1.3.31 on build.gradle file:

    dependencies {
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin: 1.3.31"
    }
    

    b) Clean and rebuild the project, to do so, execute the following command in a terminal:

    $ gradle clean build 
    

    or if using gradle wrapper:

     $ ./gradlew clean build