Edit
Adding the @ViewModelKey and making sure all the viewmodels have the @Inject annotation did the trick
Injecting ViewModels using Dagger2 Di library and ViewModelFactory causing missing binding build error.
The error I am getting is the following:
AppComponent.java:12: 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 extends dagger.android.AndroidInjector<com.honing.daggerexploration.DaggerExplorationApplication> {
^
java.util.Map<java.lang.Class<? extends androidx.lifecycle.ViewModel>,javax.inject.Provider<androidx.lifecycle.ViewModel>> is injected at
com.honing.daggerexploration.di.DaggerViewModelFactory(creators)
com.honing.daggerexploration.di.DaggerViewModelFactory is injected at
com.honing.daggerexploration.features.MainActivity.viewModelFactory
com.honing.daggerexploration.features.MainActivity is injected at
dagger.android.AndroidInjector.inject(T) [com.honing.daggerexploration.di.AppComponent → com.honing.daggerexploration.di.modules.ActivityModule_BindActivityMain.MainActivitySubcomponent]
I have searched other stackoverflow questions but non of them solved the issue for me.
Dagger version I am using is the latest, 2.22.1
I bet this error is not related to MVRX as I was able to reproduce it in small library without involving mvrx view model class, however my intentions is to eventually use dagger2 with mvrx framework and be able to inject dependencies to it.
Some code related to this:
DaggerExplorationApplication
class DaggerExplorationApplication : DaggerApplication() {
override fun applicationInjector(): AndroidInjector<out DaggerApplication> {
return DaggerAppComponent.builder().create(this)
}
}
DaggerViewModelFactory:
/**
* ViewModelFactory which uses Dagger to create the instances.
*/
class DaggerViewModelFactory @Inject constructor(
private val creators: @JvmSuppressWildcards Map<Class<out ViewModel>, Provider<ViewModel>>
) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
var creator: Provider<out ViewModel>? = creators[modelClass]
if (creator == null) {
for ((key, value) in creators) {
if (modelClass.isAssignableFrom(key)) {
creator = value
break
}
}
}
if (creator == null) {
throw IllegalArgumentException("Unknown model class: $modelClass")
}
try {
@Suppress("UNCHECKED_CAST")
return creator.get() as T
} catch (e: Exception) {
throw RuntimeException(e)
}
}
}
ViewModelFactoryModule
@Module
abstract class ViewModelFactoryModule {
@Binds
abstract fun bindViewModelFactory(viewModelFactory: DaggerViewModelFactory): ViewModelProvider.Factory
}
ActivityModule
@Module
abstract class ActivityModule {
@ContributesAndroidInjector(modules = [ViewModelFactoryModule::class])
abstract fun bindActivityMain(): MainActivity
}
In regards my efforts to implement mvrx with dagger, according to this I need to use AssistedInject library by square, I watched the video and fairly understand the reason behind this. Yet I am failed in making the project build because of the error described above. An interesting thread by chrisbanes also about this thing is on this link
MVRX ViewModels with dagger2 has been implemented successfully using this project(Tivi) by chrisbanes, I tried following what they did, but I failed too. The issue described at the top of the post is blocking me. Ready to provide any missing code, more info if needed to be able to resolve this issue.
You are missing the configuration for the map multi-binding.
Tivi has the @ViewModelKey
:
/*
* Copyright 2017 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package app.tivi.inject
import androidx.lifecycle.ViewModel
import dagger.MapKey
import kotlin.reflect.KClass
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER)
@Retention(AnnotationRetention.RUNTIME)
@MapKey
annotation class ViewModelKey(val value: KClass<out ViewModel>)
And it has a module that binds the ViewModelKey to a particular subtype of ViewModel in such a way that it is exposed as ViewModel
(and marked with a key):
/*
* Copyright 2017 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
@Binds
@IntoMap
@ViewModelKey(PopularShowsViewModel::class)
abstract fun bindPopularShowsViewModel(viewModel: PopularShowsViewModel): ViewModel
@Binds
@IntoMap
@ViewModelKey(TrendingShowsViewModel::class)
abstract fun bindTrendingShowsViewModel(viewModel: TrendingShowsViewModel): ViewModel
@Binds
@IntoMap
@ViewModelKey(ShowDetailsNavigatorViewModel::class)
abstract fun bindDetailsNavigatorViewModel(viewModel: ShowDetailsNavigatorViewModel): ViewModel
So you need to set these multi-binding configuration to a component using a module.
And it is also important that their ViewModel classes have @Inject
annotated constructor for this to work.