kotlin + Dagger 2: ApiService cannot be provided without an #Provides-annotated method - android

I have gone through all the answers with the above title but couldn't find the solution. Basically I want to do scoping. I want to inject ApiService only to HomeViewModel. It should not be available to LoginViewModel. I have my following setup and the error which I am getting:
Info: If I remove the provideLoginActivity() from ActivityModule everything works fine. Why behaving like that?
AppComponent:
#Singleton
#Component(
modules = [AndroidInjectionModule::class, ActivityModule::class, AppModule::class]
)
interface AppComponent : AndroidInjector<BaseApplication> {
#Component.Factory
interface Factory {
fun application(#BindsInstance baseApplication: BaseApplication): AppComponent
}
}
AppModule:
#Module
object AppModule {
#Singleton
#JvmStatic
#Provides
fun getRetrofit(): Retrofit {
return Retrofit.Builder()
.baseUrl("https://jsonplaceholder.typicode.com/")
.addConverterFactory(GsonConverterFactory.create())
.build()
}
}
ActivityModule:
#Module
abstract class ActivityModule {
#ContributesAndroidInjector(modules = [ViewModelBuilder::class, NetworkModule::class])
internal abstract fun getHomeActivity(): HomeActivity
#ContributesAndroidInjector(modules = [ViewModelBuilder::class])
internal abstract fun provideLoginActivity(): LoginActivity
#Binds
#IntoMap
#ViewModelKey(LoginViewModel::class)
abstract fun bindLoginViewModel(loginViewModel: LoginViewModel): ViewModel
#Binds
#IntoMap
#ViewModelKey(HomeViewModel::class)
abstract fun bindHomeViewModel(homeViewModel: HomeViewModel): ViewModel
}
NetworkModule:
#Module
object NetworkModule {
#JvmStatic
#Provides
fun getApiService(retrofit: Retrofit): ApiService {
return retrofit.create(ApiService::class.java)
}
}
ViewModelFactory:
class ViewModelFactory #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)
}
}
}
#Module
internal abstract class ViewModelBuilder {
#Binds
internal abstract fun bindViewModelFactory(
factory: ViewModelFactory
): ViewModelProvider.Factory
}
#Target(
AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER
)
#Retention(AnnotationRetention.RUNTIME)
#MapKey
annotation class ViewModelKey(val value: KClass<out ViewModel>)
HomeViewModel:
class HomeViewModel #Inject constructor(private val apiService: ApiService) : ViewModel() {
val todoLiveData: LiveData<Todo> = liveData(Dispatchers.IO) {
val response: Todo = apiService.getTodo(1)
emit(response)
}
}
Error:
error: [Dagger/MissingBinding] com.sagar.daggertest.repository.network.ApiService cannot be provided without an #Provides-annotated method.
public abstract interface AppComponent extends dagger.android.AndroidInjector<com.sagar.daggertest.BaseApplication> {
^
A binding with matching key exists in component: com.sagar.daggertest.di.HomeActivityModule_GetHomeActivity$app_debug.HomeActivitySubcomponent
com.sagar.daggertest.repository.network.ApiService is injected at
com.sagar.daggertest.HomeViewModel(apiService)
com.sagar.daggertest.HomeViewModel is injected at
com.sagar.daggertest.di.HomeActivityModule.bindHomeViewModel(homeViewModel)
java.util.Map<java.lang.Class<? extends androidx.lifecycle.ViewModel>,javax.inject.Provider<androidx.lifecycle.ViewModel>> is injected at
com.sagar.daggertest.di.ViewModelFactory(creators)
com.sagar.daggertest.di.ViewModelFactory is injected at
com.sagar.daggertest.di.ViewModelBuilder.bindViewModelFactory$app_debug(factory)
androidx.lifecycle.ViewModelProvider.Factory is injected at
com.sagar.daggertest.LoginActivity.viewModelFactory
com.sagar.daggertest.LoginActivity is injected at
dagger.android.AndroidInjector.inject(T) [com.sagar.daggertest.di.AppComponent → com.sagar.daggertest.di.HomeActivityModule_ProvideLoginActivity$app_debug.LoginActivitySubcomponent]

#ContributesAndroidInjector creates a subcomponent under the hood ( which is HomeActivityModule_GetHomeActivity$app_debug.HomeActivitySubcomponent in logs ).
In your ActivityModule, you're trying to provide HomeViewModel for map in ViewModelFactory, which is also injected at LoginActivity. But due to HomeViewModel needing ApiService and your ApiService is in NetworkModule which is scoped to subcomponent dagger generated - it fails.
Solution would be moving your multibinding to corresponding scope. By doing so, you're taking HomeViewModel out of map which is injected in LoginActivity, so it won't complain.
You can create a new module, let's say ViewModelModule and put your provider there:
#Module
abstract class ViewModelModule {
#Binds
#IntoMap
#ViewModelKey(HomeViewModel::class)
abstract fun bindHomeViewModel(homeViewModel: HomeViewModel): ViewModel
}
and pass it along with other modules to HomeActivity's contributor:
#ContributesAndroidInjector(modules = [ViewModelBuilder::class, NetworkModule::class, ViewModelModule::class])
internal abstract fun getHomeActivity(): HomeActivity

Related

Error with injecting view model using Dagger 2

I just started learning Dagger 2 and trying to make subcomponent for subcomponent. I faced error and can't find where it comes from. Below I will write my dagger structure.
AppComponent
-AppModule
-SubcomponentsModule
--MainComponent
---MainModule
----AuthorizationComponent
-----AuthorizationModule
----UserProfileComponent
-----UserProfileModule
---ViewModelModule
-NetworkModule
Application component:
#Singleton
#Component(modules = [AppModule::class, SubcomponentsModule::class, NetworkModule::class])
interface AppComponent {
fun mainComponent(): MainComponent.Factory
}
It has SubcomponentsModule with one subcomponent so far:
#Module(subcomponents = [MainComponent::class])
class SubcomponentsModule
MainComponent:
#Subcomponent(modules = [MainModule::class, ViewModelModule::class])
interface MainComponent {
#Subcomponent.Factory
interface Factory {
fun create(): MainComponent
}
fun inject(activity: MainActivity)
fun authorizationComponent(): AuthorizationComponent.Factory
fun userProfileComponent(): UserProfileComponent.Factory
}
ViewModelModule:
#Module
abstract class ViewModelModule {
#Binds
abstract fun bindViewModelFactory(factory: ViewModelFactory): ViewModelProvider.Factory
}
MainComponent has MainModule with AuthorizationComponent and UserProfileComponent:
#Module(subcomponents = [AuthorizationComponent::class, UserProfileComponent::class])
abstract class MainModule {
#Binds
#IntoMap
#ViewModelKey(MainViewModel::class)
abstract fun mainViewModel(viewModel: MainViewModel): ViewModel
}
AuthorizationComponent with AuthorizationModule:
#Subcomponent(modules = [AuthorizationModule::class])
interface AuthorizationComponent {
#Subcomponent.Factory
interface Factory {
fun create(): AuthorizationComponent
}
fun inject(fragment: AuthorizationFragment)
}
AuthorizationModule:
#Module
abstract class AuthorizationModule {
#Binds
#IntoMap
#ViewModelKey(AuthorizationViewModel::class)
abstract fun authorizationViewModel(viewModel: AuthorizationViewModel): ViewModel
}
UserProfileComponent with UserProfileModule is equivalent to AuthorizationComponent and AuthorizationModule.
When I launch build, I get error:
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
ui.base.ViewModelFactory(viewModels)
ui.base.ViewModelFactory is injected at
di.main.ViewModelModule.bindViewModelFactory(factory)
androidx.lifecycle.ViewModelProvider.Factory is injected at
ui.base.BaseActivity.viewModelFactory
ui.main.MainActivity is injected at
di.main.MainComponent.inject(ui.main.MainActivity) [di.AppComponent ? di.main.MainComponent]
The following other entry points also depend on it:
di.main.auth.AuthorizationComponent.inject(ui.auth.AuthorizationFragment) [di.AppComponent ? di.main.MainComponent ? di.main.auth.AuthorizationComponent]
di.main.userprofile.UserProfileComponent.inject(ui.userprofile.UserProfileFragment) [di.AppComponent ? di.main.MainComponent ? di.main.userprofile.UserProfileComponent]
The annotation I use for view models search is:
#Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER)
#Retention(AnnotationRetention.RUNTIME)
#MapKey
annotation class ViewModelKey(val value: KClass<out ViewModel>)
And the last thing that should be mentioned here is my ViewModelFactory:
#Singleton
class ViewModelFactory #Inject constructor(
private val viewModels: MutableMap<Class<out ViewModel>, #JvmSuppressWildcards Provider<ViewModel>>
) : ViewModelProvider.Factory {
#Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T = viewModels[modelClass]?.get() as T
}

Dagger2 Missing Binding Cannot Provides ViewModel Key

I’m trying to create dependency injection of my ViewModel using Dagger2 with multi binds but I’m receiving this error and I can’t make it work, I tried several answers (below) but none of them helped me.
This is the error I receive :
SaveMyHeroApplicationComponent.java:8: 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.
And this is my code
class SaveMyHeroApplication : DaggerApplication() {
override fun applicationInjector(): AndroidInjector<out DaggerApplication> {
return DaggerSaveMyHeroApplicationComponent.factory().create(this)
}
}
#Singleton
#Component(modules = [AndroidInjectionModule::class, MainActivityModule::class])
interface SaveMyHeroApplicationComponent : AndroidInjector<SaveMyHeroApplication> {
#Component.Factory
interface Factory {
fun create(#BindsInstance context: Context): SaveMyHeroApplicationComponent
}
}
#Module(includes = [NetworkModule::class, HomeModule::class])
class MainActivityModule {
#Provides
fun provideViewModelFactoryProviders(
providers: Map<Class<out ViewModel>, #JvmSuppressWildcards Provider<ViewModel>>
): ViewModelProvider.Factory = SaveMyHeroViewModelFactory(providers)
}
class SaveMyHeroViewModelFactory(
private val providers: Map<Class<out ViewModel>, Provider<ViewModel>>
) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T =
requireNotNull(getProvider(modelClass).get()) {
"Provider for $modelClass returned null"
}
private fun <T : ViewModel> getProvider(modelClass: Class<T>): Provider<T> =
try {
requireNotNull(providers[modelClass] as Provider<T>) {
"No ViewModel provider is bound for class $modelClass"
}
} catch (error: ClassCastException) {
error("Wrong provider type registered for ViewModel type $error")
}
}
#Module(includes = [HomeModule.ProvideViewModel::class])
abstract class HomeModule {
#ContributesAndroidInjector(modules = [InjectViewModel::class])
abstract fun bind(): HomeFragment
#Module
class ProvideViewModel {
#Provides
#IntoMap
#ViewModelKey(HomeViewModel::class)
fun provideHomeViewModel() = HomeViewModel()
}
#Module
class InjectViewModel {
#Provides
fun provideHomeViewModel(
factory: ViewModelProvider.Factory,
target: HomeFragment
) = ViewModelProvider(target, factory).get(HomeViewModel::class.java)
}
}
#MustBeDocumented
#Target(
AnnotationTarget.FUNCTION,
AnnotationTarget.PROPERTY_GETTER,
AnnotationTarget.PROPERTY_SETTER
)
#Retention(AnnotationRetention.RUNTIME)
#MapKey
annotation class ViewModelKey(val value: KClass<out ViewModel>)
Also, these are my app dependencies version:
kotlin_version = ‘1.3.72'
dagger_version = ‘2.27’
gradle:3.6.3
I know there are several questions with this problem but I tried several of them and none of them worked for me.
This are the solutions links that I tried reading and check:
https://github.com/android/architecture-components-samples/tree/master/GithubBrowserSample
https://github.com/google/dagger/issues/1478
Dagger/MissingBinding java.util.Map<java.lang.Class<? extends ViewModel>,Provider<ViewModel>> cannot be provided without an #Provides-annotated method
https://github.com/google/dagger/issues/1478
Error [Dagger/MissingBinding] androidx.lifecycle.ViewModelProvider.Factory cannot be provided without an #Provides-annotated method
https://medium.com/chili-labs/android-viewmodel-injection-with-dagger-f0061d3402ff
https://github.com/ChiliLabs/viewmodel-dagger-example
Try to use Architecture Blueprints sample (dagger-android branch) as an example.
Dagger Android is a mess itself and it's important to follow some template not to turn wrong way. May be your approach could be fixed as well, but I propose you to try change your schema:
You custom View Model factory should have #Inject in constructor:
class SaveMyHeroViewModelFactory #Inject constructor(
private val creators: #JvmSuppressWildcards Map<Class<out ViewModel>, Provider<ViewModel>>
) : ViewModelProvider.Factory {
.......
You should add auxiliary module ViewModelBuilderModule, that provides ViewModelProvider.Factory (that you inject in all your activities and fragments) with your custom ViewModel.Factory:
#Module
abstract class ViewModelBuilderModule {
#Binds
abstract fun bindViewModelFactory(factory: SaveMyHeroViewModelFactory): ViewModelProvider.Factory
}
For all your pairs - Activitiy/ViewModel and Fragment/ViewModel you should add Module like this (but you can make single module for all of them, it's up to you):
#Module
abstract class HomeModule {
#ContributesAndroidInjector(modules = [ViewModelBuilderModule::class])
internal abstract fun bind(): HomeFragment
#Binds
#IntoMap
#ViewModelKey(HomeViewModel::class)
internal abstract fun provideHomeViewModel(viewModel: HomeViewModel): ViewModel
}
In your Dagger component you should use all modules from step 3:
#Singleton
#Component(modules = [AndroidSupportInjectionModule::class, HomeModule::class, ...])
interface SaveMyHeroApplicationComponent : AndroidInjector<SaveMyHeroApplication> {
#Component.Factory
interface Factory {
fun create(#BindsInstance context: Context): SaveMyHeroApplicationComponent
}
}

How to limit scope with dagger2 about ViewModelFactory?

I'm using Android Architecture Component with Dagger2 which recommended by Google. I followed the Google Sample but it is not perfect.
This solution does simplify injection, but if my ViewModels rely on the database, a Dao was needed when create the viewmodel. For now the factory was binded to the AppModule for providing global using. So ViewModel must be provided in AppModule to adding to the map. Thus the Dao alse be global.
Since Dao may only used in some special activity, creating him globally is a waste and inconvenient to manage.
I tried to move definition of bindViewModel function to ActivityModule but errors was given as follwed:
[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.chenhe.platform.viewmodel.MyViewModelFactory(creators)
com.chenhe.platform.viewmodel.MyViewModelFactory is injected at
......
AppModule:
#Module(subcomponents = [LocalWatchFaceComponent::class], includes = [ViewModelModule::class])
class AppModule(private val context: Context) {
#Provides
#Singleton
fun provideContext(): Context = context
#Provides
#Singleton
fun provideAppDataBase(): AppDatabase = AppDatabase.getInstance(context)
// I want move code below to ActivityModule. But Dao was needed by viewmodel by factory map
#Provides
fun provideLocalWatchFaceDao(database: AppDatabase) = database.localWatchFaceDao()
}
ViewModel:
class LocalWatchFaceViewModel #Inject constructor(
private val appCtx: Context,
localWatchFaceRepository: LocalWatchFaceRepository) : ViewModel() {}
MapKey:
#MustBeDocumented
#Target(
AnnotationTarget.FUNCTION,
AnnotationTarget.PROPERTY_GETTER,
AnnotationTarget.PROPERTY_SETTER
)
#Retention(AnnotationRetention.RUNTIME)
#MapKey
annotation class ViewModelKey(val value: KClass<out ViewModel>)
ViewModelModule:
#Module
abstract class ViewModelModule {
#Binds
abstract fun bindViewModelFactory(factory: MyViewModelFactory): ViewModelProvider.Factory
#Binds
#IntoMap
#ViewModelKey(LocalWatchFaceViewModel::class)
abstract fun bindLocalWatchFaceViewModel(viewModel: LocalWatchFaceViewModel): ViewModel
}
Factory:
#Singleton
class MyViewModelFactory #Inject constructor(
private val creators: Map<Class<out ViewModel>, #JvmSuppressWildcards Provider<ViewModel>>
) : ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
val creator = creators[modelClass] ?: creators.entries.firstOrNull {
modelClass.isAssignableFrom(it.key)
}?.value ?: throw IllegalArgumentException("unknown model class $modelClass")
try {
#Suppress("UNCHECKED_CAST")
return creator.get() as T
} catch (e: Exception) {
throw RuntimeException(e)
}
}
}
Is there any plan to gracefully divide the scope?

Dagger2 compilation error in MVVM Factory when defining injector for fragment

I have a next root component:
#Singleton
#Component(modules = [AndroidInjectionModule::class,
AndroidSupportInjectionModule::class,
ActivityBuilderModule::class])
interface RootComponent : AndroidInjector<DaggerApplication> {
fun inject(myApplication: MyApplication)
override fun inject(photoPartyApplication: DaggerApplication)
#Component.Builder
interface Builder {
#BindsInstance
fun application(application: Application): Builder
fun build(): RootComponent
}
}
In ActivityBuilderModule:
#Module
abstract class ActivityBuilderModule {
#ContributesAndroidInjector(modules = [MainActivityModule::class,
ViewModelModule::class])
#ActivityScope
abstract fun bindMainActivity(): MainActivity
#ContributesAndroidInjector(modules = [SecondaryActivityModule::class,
ViewModelModule::class,
FragmentBuilderModule::class])
#ActivityScope
abstract fun bindSecondaryActivity(): SecondaryActivity
}
ViewModelModule is a trivial module to help making constructor injections in ViewModel classes and consists of #Binds between specific instances and ViewModel type.
MainActivityModule and SecondaryActivityModule define specific dependencies for corresponding activities.
The key thing is that when I added this FragmentBuilderModule - compilation started to emit errors. The stack trace is the next:
error: [Dagger/MissingBinding] some_package.SpecificDependency cannot be provided without an #Inject constructor or an #Provides-annotated method.
public abstract interface RootComponent extends dagger.android.AndroidInjector {
^
A binding with matching key exists in component: some_package.ActivityBuilderModule_BindMainActivity.MainActivitySubcomponent
some_package.SpecificDependency is injected at some_package.MainViewModel(specificDependency, …)
some_package.MainViewModel is injected at some_package.ViewModelModule.mainViewModel(viewModel)
Map<Class<? extends ViewModel>, Provider<ViewModel>> is injected at
some_package.ViewModelFactory(viewModelProviders)
some_package.ViewModelFactory is injected at some_package.ViewModelModule.bindViewModelFactory(factory)
android.arch.lifecycle.ViewModelProvider.Factory is injected at some_package.MyFragment.viewModelFactory
some_package.MyFragment is injected at dagger.android.AndroidInjector.inject(T)
[some_package.RootComponent → some_package.ActivityBuilderModule_BindSecondaryActivity.SecondaryActivitySubcomponent → some_package.FragmentBuilderModule_ProvideMyFragmentFactoryMyFragmentSubcomponent]
As far as I can understand, Dagger assumes that the whole dependencies graph has to be properly constructed for the map of Class<? extends ViewModel> -> Provider<ViewModel>, and if some ViewModels fall into factory, and that factory is injected into a component, then if component will ask for any viewmodel, it has to be delivered. And in order to deliver all the viewmodels, again, all the dependencies have to be available (what's not true, because specific dependency for the MainViewModel is available only from MainModule, and that's what dagger says before the stack trace).
Is there a workaround to provide dependencies to the map of Class<? extends ViewModel> -> Provider<ViewModel> on demand instead of building the whole graph at compile time (which is leading to the compile-time error)
First add ViewModelModule at RootComponent
#Singleton
#Component(modules = [AndroidInjectionModule::class,
AndroidSupportInjectionModule::class,
ActivityBuilderModule::class,
ViewModelModule::class]) // add this so you don't have to add for every activity
interface RootComponent : AndroidInjector<DaggerApplication> {
fun inject(myApplication: MyApplication)
override fun inject(photoPartyApplication: DaggerApplication)
#Component.Builder
interface Builder {
#BindsInstance
fun application(application: Application): Builder
fun build(): RootComponent
}
}
Now in ActivityBuilderModule add only activty
#Module
abstract class ActivityBuilderModule {
#ContributesAndroidInjector
#ActivityScope
abstract fun bindMainActivity(): MainActivity
}
Now In ViewModelModule add all ViewModel
#Module
abstract class ViewModelModule {
#Binds
#IntoMap
#ViewModelKey(MainActivityModule::class)
abstract fun bindMainActivityViewModel(mainActivityViewModel: MainActivityModule): ViewModel
}
Add ViewModelKey
#MustBeDocumented
#kotlin.annotation.Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER)
#kotlin.annotation.Retention(AnnotationRetention.RUNTIME)
#MapKey
internal annotation class ViewModelKey(val value: KClass<out ViewModel>)
And ViewModelFactory
#Singleton
class KotlinViewModelFactory #Inject
constructor(private val creators: Map<Class<out ViewModel>, #JvmSuppressWildcards Provider<ViewModel>>) : ViewModelProvider.Factory {
#Suppress("UNCHECKED_CAST")
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 {
Timber.d(creator.toString())
return creator.get() as T
} catch (e: Exception) {
throw RuntimeException(e)
}
}
}

Android MVVM Dagger 2 - ViewModelFactory Injection

I am trying to perform viewmodel injection with dagger 2 in Android Kotlin project. So far my project looks like this. I have AppComponent looking like this
#AppScope
#Component(modules = [
ViewModelModule::class,
AndroidSupportInjectionModule::class,
AppModule::class,
BuildersModule::class
])
interface AppComponent {
#Component.Builder
interface Builder {
#BindsInstance
fun application(application : App) : Builder
fun build() : AppComponent
}
fun inject(app: App)
}
My appModule:
#Module class AppModule
I created also Builders module for providing my Views:
#Module
abstract class BuildersModule {
#ContributesAndroidInjector
abstract fun providesMainActivity() : MainActivity
#ContributesAndroidInjector()
abstract fun providesModeMenuActivity(): ModeMenuActivity
}
My view model facotory is taken from example on github project
#AppScope
class ViewModelFactory
#Inject constructor(
private val creators: Map<Class<out ViewModel>, #JvmSuppressWildcards Provider<ViewModel>>
) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
val creator = creators[modelClass] ?:
creators.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)
}
}
}
I bound factory in my ViewModelModule like this:
#Module
abstract class ViewModelModule {
#Binds
#IntoMap
#ViewModelKey(ModeMenuViewModel::class)
abstract fun bindModeMenuViewModel(modeMenuViewModel: ModeMenuViewModel): ModeMenuViewModel
#Binds
abstract fun bindsViewModelFactory(factory: ViewModelFactory): ViewModelProvider.Factory
}
Now everything builds until I add ViewModelFacotory injection in one of activities like this:
class ModeMenuActivity: AppCompatActivity() {
#Inject
lateinit var vmFactory: ViewModelProvider.Factory
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
AndroidInjection.inject(this)
val binding: ActivityModeMenuBinding = DataBindingUtil.setContentView(this, R.layout.activity_mode_menu)
val viewModel = ViewModelProviders.of(this, vmFactory).get(ModeMenuViewModel::class.java)
binding.ViewModel = viewModel
}
}
When I build code afret adding #Inject I get following error:
C:\Users\Maciej\AndroidStudioProjects\AndroidMVVM\app\build\tmp\kapt3\stubs\debug\com\example\maciej\androidmvvm\di\AppComponent.java:8: error: [Dagger/MissingBinding] [dagger.android.AndroidInjector.inject(T)] java.util.Map<java.lang.Class<? extends android.arch.lifecycle.ViewModel>,javax.inject.Provider<android.arch.lifecycle.ViewModel>> cannot be provided without an #Provides-annotated method.
public abstract interface AppComponent {
^
java.util.Map<java.lang.Class<? extends android.arch.lifecycle.ViewModel>,javax.inject.Provider<android.arch.lifecycle.ViewModel>> is injected at
com.example.maciej.androidmvvm.ViewModelModule.ViewModelFactory.<init>(creators)
com.example.maciej.androidmvvm.ViewModelModule.ViewModelFactory is injected at
com.example.maciej.androidmvvm.ViewModelModule.ViewModelModule.bindsViewModelFactory(factory)
android.arch.lifecycle.ViewModelProvider.Factory is injected at
com.example.maciej.androidmvvm.ui.common.ModeMenu.ModeMenuActivity.vmFactory
com.example.maciej.androidmvvm.ui.common.ModeMenu.ModeMenuActivity is injected at
dagger.android.AndroidInjector.inject(T)
component path: com.example.maciej.androidmvvm.di.AppComponent ? com.example.maciej.androidmvvm.di.BuildersModule_ProvidesModeMenuActivity.
So far I couldn't find anything at forums so I would be thankfull if you could show my what am I doing wrong. Also I noticed that when I try to attach view model to my binding object in Activity I get type missmatch (it shows incomplete package name as required type)
Your binding is a no op since you're returning the same type you're passing as argument, so change return type like this
Before:
#Binds
#IntoMap
#ViewModelKey(ModeMenuViewModel::class)
abstract fun bindModeMenuViewModel(modeMenuViewModel: ModeMenuViewModel): ModeMenuViewModel
After:
Binds
#IntoMap
#ViewModelKey(ModeMenuViewModel::class)
abstract fun bindModeMenuViewModel(modeMenuViewModel: ModeMenuViewModel): ViewModel
Finally I found solution. Dagger had problems with my packages names started from big letters. When I changed all packages names to camel case everything started to work.

Categories

Resources