google / dagger

A fast dependency injector for Android and Java.
https://dagger.dev
Apache License 2.0
17.42k stars 2.01k forks source link

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

Open phongbm opened 3 years ago

phongbm commented 3 years ago

I used Dagger2 for my project as a long time. I set up ViewModel with Dagger2 like this post.

Today, I use Hilt for my project in a specific dynamic feature module, (don't :app module). In :app module, I put AndroidEntryPoint for MainActivity, and project can't build with log

^C:\Data\Projects\Moola\Rental\AndroidMoolaPro\app\build\generated\source\kapt\devDebug\com\moola\pro\App_HiltComponents.java:122: error: [Dagger/MissingBinding] androidx.lifecycle.ViewModelProvider.Factory cannot be provided without an @Provides-annotated method.
  public abstract static class SingletonC implements App_GeneratedInjector,
                         ^
      androidx.lifecycle.ViewModelProvider.Factory is injected at
          com.moola.pro.base.BaseActivity.viewModelFactory
      com.moola.pro.view.MainActivity is injected at
          com.moola.pro.view.MainActivity_GeneratedInjector.injectMainActivity(com.moola.pro.view.MainActivity) [com.moola.pro.App_HiltComponents.SingletonC ? com.moola.pro.App_HiltComponents.ActivityRetainedC ? com.moola.pro.App_HiltComponents.ActivityC]
> Task :app:kaptDevDebugKotlin FAILED

Execution failed for task ':app:kaptDevDebugKotlin'.
> A failure occurred while executing org.jetbrains.kotlin.gradle.internal.KaptWithoutKotlincTask$KaptExecutionWorkAction
   > java.lang.reflect.InvocationTargetException (no error message)

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

With only Dagger2, everything work well, but I added Hilt (project has both Dagger and Hilt), it has bug.

Please help me. Thanks.

phongbm commented 3 years ago

Somebody please help me. I'm stuck here.

Chang-Eric commented 3 years ago

It is hard to say without a code sample what is going on, but Hilt doesn't really support dynamic feature modules. What is likely going on is that whatever module you have that provides ViewModelProvider.Factory is in the dynamic feature module, which means Hilt isn't going to find it even if you have @InstallIn on it since it isn't built with the rest of the application. You probably need to move that module to your base feature module.

There's a page on this here that may be helpful: https://developer.android.com/training/dependency-injection/hilt-multi-module

phongbm commented 3 years ago

The weird thing is that I can't build the app. Without using Hilt, the project still works fine with Dagger2. My project is large, has been active for a long time, there is only one module is :app. Now, I want to separate each feature into an independent module so I use dynamic feature module. In the :app module, in MainActivity, I use @AndroidEntryPoint and project can't build.

Since I don't want to edit the code already in the project, I use "Turning off the @InstallIn check" for the existing @Modules, like this post

In :app module

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 {
            @Suppress("UNCHECKED_CAST")
            creator.get() as T
        } catch (e: Exception) {
            throw RuntimeException(e)
        }
    }

}
@Module
abstract class ViewModelModule {
    @Binds
    abstract fun bindViewModelFactory(factory: ViewModelFactory): ViewModelProvider.Factory

    @Binds
    @IntoMap
    @ViewModelKey(MainViewModel::class)
    abstract fun bindMainViewModel(viewModel: MainViewModel): ViewModel
}
phongbm commented 3 years ago

The :app module use Dagger2 still, but I want new dynamic feature module use Hilt. For Hilt to work, in :app, class App put @HiltAndroidApp, class MainActivity put @AndroidEntryPoint, after that, project can't build.

phongbm commented 3 years ago

I create this project for demo my issue. With demo, it can't build too, error: DaggerApp_HiltComponents_SingletonC$ActivityRetainedCImpl$ActivityCImpl$FragmentCI cannot be cast to... If you have time, please check it out for support me. Thank you.

Chang-Eric commented 3 years ago

Hm I built your demo and it builds successfully. (I did have to delete getDefaultProguardFile('proguard-android-optimize.txt') to get by a lint issue, but after that there were no problems). Can you double check that you still get a build error or can you add more of the error you get in here?

TienNguyenVan1408 commented 3 years ago

I'm facing the same issue when injecting FramgetActivity into handle class. Can you tell me the reason for this issue?. Thank you so much @Chang-Eric

phongbm commented 3 years ago

Hm I built your demo and it builds successfully. (I did have to delete getDefaultProguardFile('proguard-android-optimize.txt') to get by a lint issue, but after that there were no problems). Can you double check that you still get a build error or can you add more of the error you get in here?

Sory for the late reply. I have updated the project. It builds successfully but when run it throws an error (click button "Open Feature").

Thank for your support.