Hey all I am having problems figuring out hilt with android. Ive googled around and I can't find this specific problem. I seem to getting a "View Model has no zero argument constructor". I saw on another post that it was for a missing @AndroidEntryPoint annotation in their main activity however I added that and at this point I am a little stumped.
So far I have an application class
@HiltAndroidApp
class RecipeApplication: Application() {
}
The main activity
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MaterialTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = primaryBackgroundColor
) {
RecipeBookApp()
}
}
}
}
}
The View model
@HiltViewModel
class RecipeViewModel @Inject constructor(
private val recipeRepo: RecipeRepo
) : ViewModel() {....}
and then the Composable setting up my UI.
@Composable
fun RecipeBookApp(
navController: NavHostController = rememberNavController()
) {
Log.d("GetRecipe", "Still in Recipe Screen About to make the call")
val recipeViewModel = viewModel(modelClass = RecipeViewModel::class.java)
var editMode by remember { mutableStateOf(false) }
Scaffold(
backgroundColor = primaryBackgroundColor,
topBar = {
val title = "Edit"
Column {
Row(Modifier.padding(10.dp)) {
NiceButton(title = title) {
editMode = !editMode
}
}
}
},
bottomBar = {
}) { innerPadding ->
NavHost(
navController,
RecipeScreen.Start.name
) {
composable(route = RecipeScreen.Start.name) {
RecipeGridScreen(
onGridButtonClick = {
navController.navigate(RecipeScreen.RecipePage.name)
}
)
}
composable(route = RecipeScreen.RecipePage.name) {
val state = recipeViewModel.recipeState.collectAsState()
RecipeView(state.value)
}
}
}
}
RecipeRepo
class RecipeRepo @Inject constructor(
private val recipeApi: RecipeApi
) {
suspend fun getAllRecipes(): RecipeList {
return recipeApi.getRecipes()
}
}
RecipeApi
interface RecipeApi {
@GET("recipe/getRecipes")
suspend fun getRecipes(): RecipeList
}
RecipeApiModule
@Module
@InstallIn(SingletonComponent::class)
object RecipeApiModule {
@Provides
@Singleton
fun provideApi(builder: Retrofit.Builder): RecipeApi {
return builder
.build()
.create(RecipeApi::class.java)
}
@Provides
@Singleton
fun provideRetrofit(): Retrofit.Builder {
return Retrofit.Builder()
.baseUrl(RECIPE_URL)
.addConverterFactory(GsonConverterFactory.create())
}
}
Process: com.bunkware.bunkyrecipe, PID: 6287 java.lang.RuntimeException: Cannot create an instance of class com.bunkware.bunkyrecipe.ui.recipe.RecipeViewModel at androidx.lifecycle.ViewModelProvider$NewInstanceFactory.create(ViewModelProvider.kt:204) at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.kt:322) at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.kt:304) at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.kt:278) at androidx.lifecycle.SavedStateViewModelFactory.create(SavedStateViewModelFactory.kt:128) at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.kt:187) at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.kt:153) at androidx.lifecycle.viewmodel.compose.ViewModelKt.get(ViewModel.kt:215) at androidx.lifecycle.viewmodel.compose.ViewModelKt.viewModel(ViewModel.kt:156) at com.bunkware.bunkyrecipe.ui.RecipeGridScreenKt.RecipeGridScreen(RecipeGridScreen.kt:30) at com.bunkware.bunkyrecipe.ui.recipe.RecipeScreenKt$RecipeBookApp$2$1$1.invoke(RecipeScreen.kt:63) at com.bunkware.bunkyrecipe.ui.recipe.RecipeScreenKt$RecipeBookApp$2$1$1.invoke(RecipeScreen.kt:62) at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:116) at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34) at androidx.navigation.compose.NavHostKt$NavHost$4$2.invoke(NavHost.kt:163) at androidx.navigation.compose.NavHostKt$NavHost$4$2.invoke(NavHost.kt:162) at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107) at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34) at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:228) at androidx.compose.runtime.saveable.SaveableStateHolderImpl.SaveableStateProvider(SaveableStateHolder.kt:84) at androidx.navigation.compose.NavBackStackEntryProviderKt.SaveableStateProvider(NavBackStackEntryProvider.kt:65) at androidx.navigation.compose.NavBackStackEntryProviderKt.access$SaveableStateProvider(NavBackStackEntryProvider.kt:1) at androidx.navigation.compose.NavBackStackEntryProviderKt$LocalOwnersProvider$1.invoke(NavBackStackEntryProvider.kt:52) at androidx.navigation.compose.NavBackStackEntryProviderKt$LocalOwnersProvider$1.invoke(NavBackStackEntryProvider.kt:51) at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107) at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34) at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:228) at androidx.navigation.compose.NavBackStackEntryProviderKt.LocalOwnersProvider(NavBackStackEntryProvider.kt:47) at androidx.navigation.compose.NavHostKt$NavHost$4.invoke(NavHost.kt:162) at androidx.navigation.compose.NavHostKt$NavHost$4.invoke(NavHost.kt:141) at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:116) at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34) at androidx.compose.animation.CrossfadeKt$Crossfade$5$1.invoke(Crossfade.kt:133) at androidx.compose.animation.CrossfadeKt$Crossfade$5$1.invoke(Crossfade.kt:128) at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107) at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34) at androidx.compose.animation.CrossfadeKt.Crossfade(Crossfade.kt:142) at androidx.compose.animation.CrossfadeKt.Crossfade(Crossfade.kt:73) at androidx.navigation.compose.NavHostKt.NavHost(NavHost.kt:141) at androidx.navigation.compose.NavHostKt$NavHost$5.invoke(Unknown Source:13) at androidx.navigation.compose.NavHostKt$NavHost$5.invoke(Unknown Source:10) at androidx.compose.runtime.RecomposeScopeImpl.compose(RecomposeScopeImpl.kt:145) 2023-02-04 11:45:31.431 6287-6287 AndroidRuntime
com.bunkware.bunkyrecipe E at androidx.compose.runtime.ComposerImpl.recomposeToGroupEnd(Composer.kt:2375) at androidx.compose.runtime.ComposerImpl.skipCurrentGroup(Composer.kt:2643) at androidx.compose.runtime.ComposerImpl$doCompose$2$5.invoke(Composer.kt:3260) at androidx.compose.runtime.ComposerImpl$doCompose$2$5.invoke(Composer.kt:3238) at androidx.compose.runtime.SnapshotStateKt__DerivedStateKt.observeDerivedStateRecalculations(DerivedState.kt:341) at androidx.compose.runtime.SnapshotStateKt.observeDerivedStateRecalculations(Unknown Source:1) at androidx.compose.runtime.ComposerImpl.doCompose(Composer.kt:3238) at androidx.compose.runtime.ComposerImpl.recompose$runtime_release(Composer.kt:3203) at androidx.compose.runtime.CompositionImpl.recompose(Composition.kt:771) at androidx.compose.runtime.Recomposer.performRecompose(Recomposer.kt:1031) at androidx.compose.runtime.Recomposer.access$performRecompose(Recomposer.kt:125) at androidx.compose.runtime.Recomposer$runRecomposeAndApplyChanges$2$2.invoke(Recomposer.kt:534) at androidx.compose.runtime.Recomposer$runRecomposeAndApplyChanges$2$2.invoke(Recomposer.kt:503) at androidx.compose.ui.platform.AndroidUiFrameClock$withFrameNanos$2$callback$1.doFrame(AndroidUiFrameClock.android.kt:34) at androidx.compose.ui.platform.AndroidUiDispatcher.performFrameDispatch(AndroidUiDispatcher.android.kt:109) at androidx.compose.ui.platform.AndroidUiDispatcher.access$performFrameDispatch(AndroidUiDispatcher.android.kt:41) at androidx.compose.ui.platform.AndroidUiDispatcher$dispatchCallback$1.doFrame(AndroidUiDispatcher.android.kt:69) at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1229) at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1239) at android.view.Choreographer.doCallbacks(Choreographer.java:899) at android.view.Choreographer.doFrame(Choreographer.java:827) at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1214) at android.os.Handler.handleCallback(Handler.java:942) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loopOnce(Looper.java:201) at android.os.Looper.loop(Looper.java:288) at android.app.ActivityThread.main(ActivityThread.java:7872) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936) Suppressed: kotlinx.coroutines.DiagnosticCoroutineContextException: [androidx.compose.runtime.PausableMonotonicFrameClock@dfc23f4, androidx.compose.ui.platform.MotionDurationScaleImpl@a07d71d, StandaloneCoroutine{Cancelling}@64f9d92, AndroidUiDispatcher@2d4c363] Caused by: java.lang.InstantiationException: java.lang.Class<com.bunkware.bunkyrecipe.ui.recipe.RecipeViewModel> has no zero argument constructor at java.lang.Class.newInstance(Native Method) at androidx.lifecycle.ViewModelProvider$NewInstanceFactory.create(ViewModelProvider.kt:202) ... 71 more
CodePudding user response:
Your error message says RecipeGridScreen
inside your NavHost
is calling viewModel()
. As per the Hilt and Navigation Compose docs, you must always use hiltViewModel()
when inside a NavHost
so that Hilt can find the correct factory.
So:
- Add the
hilt-navigation-compose
dependency to yourbuild.gradle
file:
implementation("androidx.hilt:hilt-navigation-compose:1.0.0")
- Change all calls to
viewModel()
tohiltViewModel()
. This is only required inside yourNavHost
, but it works everywhere, so you can do a find/replace of all instances if you'd like.