Hilt viewModel 和 Compose 不能一起工作
Hilt viewModel and Compose not working together
官方 Android docs 使用 Compose、Hilt 和 ViewModel 注入 Navigation 的方法似乎不起作用。
我有以下设置:
一个主屏幕,仅包含导航控制器。
主屏幕显示一个按钮。导航到具有视图模型的名称屏幕。
@Composable
fun MainScreen() {
val navController = rememberNavController()
NavHost(navController, startDestination = "home") {
composable("home") {
HomeScreen() {
navController.navigate("my/Name")
}
}
composable("my/{name}") { navBackStackEntry ->
val viewModel: NameScreenViewModel = hiltViewModel(navBackStackEntry)
NameScreen(viewModel)
}
}
}
@Composable
fun HomeScreen(onButtonClicked: () -> Unit) {
Button(onClick = onButtonClicked, modifier = Modifier.padding(8.dp)) {
Text("Continue")
}
}
@HiltViewModel
class NameScreenViewModel @Inject constructor(private val test: TestClass): ViewModel() {
var name = MutableLiveData("empty")
fun change() {
name.value = test.load()
}
}
@Composable
fun NameScreen(viewModel: NameScreenViewModel) {
val name by viewModel.name.observeAsState()
Column {
Text("Hi there, $name")
Button(onClick = { viewModel.change() }) {
Text("Click me!")
}
}
}
Activity 和应用程序具有正确的注释:
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ComposeViewModelTheme {
// A surface container using the 'background' color from the theme
Surface(color = MaterialTheme.colors.background) {
MainScreen()
}
}
}
}
}
@HiltAndroidApp
class MainApplication : Application() {
}
注入的class非常简单,甚至注册为提供而不是使用构造函数注解:
class TestClass {
fun load(): String {
return "name"
}
}
@Module
@InstallIn(SingletonComponent::class)
class AppModule {
@Provides
@Singleton
fun providesTestClass(): TestClass {
return TestClass()
}
}
如果像上面那样使用hiltViewModel
,应用程序将崩溃并出现错误
java.lang.IllegalStateException: Given component holder class eu.meecolabs.composeviewmodel.MainActivity does not implement interface dagger.hilt.internal.GeneratedComponent or interface dagger.hilt.internal.GeneratedComponentManager
at dagger.hilt.EntryPoints.get(EntryPoints.java:62)
at dagger.hilt.android.internal.lifecycle.HiltViewModelFactory.createInternal(HiltViewModelFactory.java:129)
at androidx.hilt.navigation.HiltViewModelFactory.create(HiltNavBackStackEntry.kt:56)
at eu.meecolabs.composeviewmodel.ui.ComposableSingletons$MainScreenKt$lambda-1.invoke(MainScreen.kt:26)
at eu.meecolabs.composeviewmodel.ui.ComposableSingletons$MainScreenKt$lambda-1.invoke(MainScreen.kt:25)
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.invoke(NavHost.kt:146)
at androidx.navigation.compose.NavHostKt$NavHost.invoke(NavHost.kt:145)
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:215)
at androidx.compose.runtime.saveable.SaveableStateHolderImpl.SaveableStateProvider(SaveableStateHolder.kt:84)
at androidx.navigation.compose.NavBackStackEntryProviderKt.SaveableStateProvider(NavBackStackEntryProvider.kt:59)
at androidx.navigation.compose.NavBackStackEntryProviderKt.access$SaveableStateProvider(NavBackStackEntryProvider.kt:1)
at androidx.navigation.compose.NavBackStackEntryProviderKt$LocalOwnersProvider.invoke(NavBackStackEntryProvider.kt:51)
at androidx.navigation.compose.NavBackStackEntryProviderKt$LocalOwnersProvider.invoke(NavBackStackEntryProvider.kt:50)
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:215)
at androidx.navigation.compose.NavBackStackEntryProviderKt.LocalOwnersProvider(NavBackStackEntryProvider.kt:46)
at androidx.navigation.compose.NavHostKt$NavHost.invoke(NavHost.kt:145)
at androidx.navigation.compose.NavHostKt$NavHost.invoke(NavHost.kt:144)
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.invoke(Crossfade.kt:74)
at androidx.compose.animation.CrossfadeKt$Crossfade.invoke(Crossfade.kt:69)
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:86)
at androidx.navigation.compose.NavHostKt.NavHost(NavHost.kt:144)
at androidx.navigation.compose.NavHostKt$NavHost.invoke(Unknown Source:13)
at androidx.navigation.compose.NavHostKt$NavHost.invoke(Unknown Source:10)
at androidx.compose.runtime.RecomposeScopeImpl.compose(RecomposeScopeImpl.kt:140)
at androidx.compose.runtime.ComposerImpl.recomposeToGroupEnd(Composer.kt:2156)
at androidx.compose.runtime.ComposerImpl.skipCurrentGroup(Composer.kt:2399)
at androidx.compose.runtime.ComposerImpl$doCompose.invoke(Composer.kt:2580)
at androidx.compose.runtime.ComposerImpl$doCompose.invoke(Composer.kt:2573)
at androidx.compose.runtime.SnapshotStateKt.observeDerivedStateRecalculations(SnapshotState.kt:540)
at androidx.compose.runtime.ComposerImpl.doCompose(Composer.kt:2566)
2021-07-27 17:46:40.678 22501-22501/? E/AndroidRuntime: at androidx.compose.runtime.ComposerImpl.recompose$runtime_release(Composer.kt:2542)
at androidx.compose.runtime.CompositionImpl.recompose(Composition.kt:613)
at androidx.compose.runtime.Recomposer.performRecompose(Recomposer.kt:764)
at androidx.compose.runtime.Recomposer.access$performRecompose(Recomposer.kt:103)
at androidx.compose.runtime.Recomposer$runRecomposeAndApplyChanges.invoke(Recomposer.kt:447)
at androidx.compose.runtime.Recomposer$runRecomposeAndApplyChanges.invoke(Recomposer.kt:416)
at androidx.compose.ui.platform.AndroidUiFrameClock$withFrameNanos$callback.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.doFrame(AndroidUiDispatcher.android.kt:69)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:965)
at android.view.Choreographer.doCallbacks(Choreographer.java:791)
at android.view.Choreographer.doFrame(Choreographer.java:722)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:952)
at android.os.Handler.handleCallback(Handler.java:883)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7356)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:491)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:940)
我正在使用最新的 Android Studio Preview 和以下依赖版本:
ext {
compose_version = '1.0.0-rc02'
hilt_version = '2.38'
}
dependencies {
classpath "com.android.tools.build:gradle:7.1.0-alpha04"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.10"
classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
implementation 'androidx.core:core-ktx:1.6.0'
implementation 'androidx.appcompat:appcompat:1.3.1'
implementation 'com.google.android.material:material:1.4.0'
implementation "androidx.compose.ui:ui:$compose_version"
implementation "androidx.compose.material:material:$compose_version"
implementation "androidx.compose.ui:ui-tooling-preview:$compose_version"
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1'
implementation "androidx.compose.runtime:runtime-livedata:$compose_version"
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.3.1'
implementation 'androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha03'
implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:1.0.0-alpha07'
implementation 'androidx.activity:activity-compose:1.3.0-rc02'
implementation "androidx.navigation:navigation-compose:2.4.0-alpha05"
implementation 'androidx.hilt:hilt-navigation-compose:1.0.0-alpha03'
implementation "com.google.dagger:hilt-android:$hilt_version"
kapt "com.google.dagger:hilt-compiler:$hilt_version"
kapt 'androidx.hilt:hilt-compiler:1.0.0'
我真的不知道还能尝试什么。我已经检查了很多不同的组合,并通读了全文或博客文章或 SO 问题,但似乎都在使用过时的库或似乎没有真正的解决方案。
我用下面的方法解决了这个问题
class MyScreenManager(navController:NavigationController){
lateinit var viewModel:MyViewModel = HiltViewModelFactory(
activity,
navController.getBackStackEntry(MyScreen.route)
).create(MyViewModel::class.java)
}
托管此代码的 class 未注入但已导航至。
解决方案似乎是更新依赖项。由于我发布了这个问题,Hilt 发布了 v2.38.1,Compose 发布了 v1.0.0(最终版)。
该组合将按预期工作。
官方 Android docs 使用 Compose、Hilt 和 ViewModel 注入 Navigation 的方法似乎不起作用。
我有以下设置:
一个主屏幕,仅包含导航控制器。 主屏幕显示一个按钮。导航到具有视图模型的名称屏幕。
@Composable
fun MainScreen() {
val navController = rememberNavController()
NavHost(navController, startDestination = "home") {
composable("home") {
HomeScreen() {
navController.navigate("my/Name")
}
}
composable("my/{name}") { navBackStackEntry ->
val viewModel: NameScreenViewModel = hiltViewModel(navBackStackEntry)
NameScreen(viewModel)
}
}
}
@Composable
fun HomeScreen(onButtonClicked: () -> Unit) {
Button(onClick = onButtonClicked, modifier = Modifier.padding(8.dp)) {
Text("Continue")
}
}
@HiltViewModel
class NameScreenViewModel @Inject constructor(private val test: TestClass): ViewModel() {
var name = MutableLiveData("empty")
fun change() {
name.value = test.load()
}
}
@Composable
fun NameScreen(viewModel: NameScreenViewModel) {
val name by viewModel.name.observeAsState()
Column {
Text("Hi there, $name")
Button(onClick = { viewModel.change() }) {
Text("Click me!")
}
}
}
Activity 和应用程序具有正确的注释:
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ComposeViewModelTheme {
// A surface container using the 'background' color from the theme
Surface(color = MaterialTheme.colors.background) {
MainScreen()
}
}
}
}
}
@HiltAndroidApp
class MainApplication : Application() {
}
注入的class非常简单,甚至注册为提供而不是使用构造函数注解:
class TestClass {
fun load(): String {
return "name"
}
}
@Module
@InstallIn(SingletonComponent::class)
class AppModule {
@Provides
@Singleton
fun providesTestClass(): TestClass {
return TestClass()
}
}
如果像上面那样使用hiltViewModel
,应用程序将崩溃并出现错误
java.lang.IllegalStateException: Given component holder class eu.meecolabs.composeviewmodel.MainActivity does not implement interface dagger.hilt.internal.GeneratedComponent or interface dagger.hilt.internal.GeneratedComponentManager
at dagger.hilt.EntryPoints.get(EntryPoints.java:62)
at dagger.hilt.android.internal.lifecycle.HiltViewModelFactory.createInternal(HiltViewModelFactory.java:129)
at androidx.hilt.navigation.HiltViewModelFactory.create(HiltNavBackStackEntry.kt:56)
at eu.meecolabs.composeviewmodel.ui.ComposableSingletons$MainScreenKt$lambda-1.invoke(MainScreen.kt:26)
at eu.meecolabs.composeviewmodel.ui.ComposableSingletons$MainScreenKt$lambda-1.invoke(MainScreen.kt:25)
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.invoke(NavHost.kt:146)
at androidx.navigation.compose.NavHostKt$NavHost.invoke(NavHost.kt:145)
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:215)
at androidx.compose.runtime.saveable.SaveableStateHolderImpl.SaveableStateProvider(SaveableStateHolder.kt:84)
at androidx.navigation.compose.NavBackStackEntryProviderKt.SaveableStateProvider(NavBackStackEntryProvider.kt:59)
at androidx.navigation.compose.NavBackStackEntryProviderKt.access$SaveableStateProvider(NavBackStackEntryProvider.kt:1)
at androidx.navigation.compose.NavBackStackEntryProviderKt$LocalOwnersProvider.invoke(NavBackStackEntryProvider.kt:51)
at androidx.navigation.compose.NavBackStackEntryProviderKt$LocalOwnersProvider.invoke(NavBackStackEntryProvider.kt:50)
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:215)
at androidx.navigation.compose.NavBackStackEntryProviderKt.LocalOwnersProvider(NavBackStackEntryProvider.kt:46)
at androidx.navigation.compose.NavHostKt$NavHost.invoke(NavHost.kt:145)
at androidx.navigation.compose.NavHostKt$NavHost.invoke(NavHost.kt:144)
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.invoke(Crossfade.kt:74)
at androidx.compose.animation.CrossfadeKt$Crossfade.invoke(Crossfade.kt:69)
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:86)
at androidx.navigation.compose.NavHostKt.NavHost(NavHost.kt:144)
at androidx.navigation.compose.NavHostKt$NavHost.invoke(Unknown Source:13)
at androidx.navigation.compose.NavHostKt$NavHost.invoke(Unknown Source:10)
at androidx.compose.runtime.RecomposeScopeImpl.compose(RecomposeScopeImpl.kt:140)
at androidx.compose.runtime.ComposerImpl.recomposeToGroupEnd(Composer.kt:2156)
at androidx.compose.runtime.ComposerImpl.skipCurrentGroup(Composer.kt:2399)
at androidx.compose.runtime.ComposerImpl$doCompose.invoke(Composer.kt:2580)
at androidx.compose.runtime.ComposerImpl$doCompose.invoke(Composer.kt:2573)
at androidx.compose.runtime.SnapshotStateKt.observeDerivedStateRecalculations(SnapshotState.kt:540)
at androidx.compose.runtime.ComposerImpl.doCompose(Composer.kt:2566)
2021-07-27 17:46:40.678 22501-22501/? E/AndroidRuntime: at androidx.compose.runtime.ComposerImpl.recompose$runtime_release(Composer.kt:2542)
at androidx.compose.runtime.CompositionImpl.recompose(Composition.kt:613)
at androidx.compose.runtime.Recomposer.performRecompose(Recomposer.kt:764)
at androidx.compose.runtime.Recomposer.access$performRecompose(Recomposer.kt:103)
at androidx.compose.runtime.Recomposer$runRecomposeAndApplyChanges.invoke(Recomposer.kt:447)
at androidx.compose.runtime.Recomposer$runRecomposeAndApplyChanges.invoke(Recomposer.kt:416)
at androidx.compose.ui.platform.AndroidUiFrameClock$withFrameNanos$callback.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.doFrame(AndroidUiDispatcher.android.kt:69)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:965)
at android.view.Choreographer.doCallbacks(Choreographer.java:791)
at android.view.Choreographer.doFrame(Choreographer.java:722)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:952)
at android.os.Handler.handleCallback(Handler.java:883)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7356)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:491)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:940)
我正在使用最新的 Android Studio Preview 和以下依赖版本:
ext {
compose_version = '1.0.0-rc02'
hilt_version = '2.38'
}
dependencies {
classpath "com.android.tools.build:gradle:7.1.0-alpha04"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.10"
classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
implementation 'androidx.core:core-ktx:1.6.0'
implementation 'androidx.appcompat:appcompat:1.3.1'
implementation 'com.google.android.material:material:1.4.0'
implementation "androidx.compose.ui:ui:$compose_version"
implementation "androidx.compose.material:material:$compose_version"
implementation "androidx.compose.ui:ui-tooling-preview:$compose_version"
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1'
implementation "androidx.compose.runtime:runtime-livedata:$compose_version"
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.3.1'
implementation 'androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha03'
implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:1.0.0-alpha07'
implementation 'androidx.activity:activity-compose:1.3.0-rc02'
implementation "androidx.navigation:navigation-compose:2.4.0-alpha05"
implementation 'androidx.hilt:hilt-navigation-compose:1.0.0-alpha03'
implementation "com.google.dagger:hilt-android:$hilt_version"
kapt "com.google.dagger:hilt-compiler:$hilt_version"
kapt 'androidx.hilt:hilt-compiler:1.0.0'
我真的不知道还能尝试什么。我已经检查了很多不同的组合,并通读了全文或博客文章或 SO 问题,但似乎都在使用过时的库或似乎没有真正的解决方案。
我用下面的方法解决了这个问题
class MyScreenManager(navController:NavigationController){
lateinit var viewModel:MyViewModel = HiltViewModelFactory(
activity,
navController.getBackStackEntry(MyScreen.route)
).create(MyViewModel::class.java)
}
托管此代码的 class 未注入但已导航至。
解决方案似乎是更新依赖项。由于我发布了这个问题,Hilt 发布了 v2.38.1,Compose 发布了 v1.0.0(最终版)。
该组合将按预期工作。