为什么当视图模型中的状态发生变化时,路由的可组合项会重组?
Why does the Composable for the route recompose when state changes in the view model?
TLDR:问题是,当 composable
不依赖于我的 ViewModel 中更新的值时,为什么我在 logcat 中同时看到“可组合路由”和“主屏幕”?我预计只会看到“主屏幕”,因为这是唯一使用来自 ViewModel 的状态值的可组合项。
我最近在生产应用程序中遇到一种情况,我的路由可组合项被调用了两次,导致 UI 出现轻微闪烁。深入研究了几个小时后,我发现闪烁的原因是在为我的撰写屏幕提供 ViewModel 的路由中使用 hiltViewModel
。我进一步挖掘才发现,只要 ViewModel 中的状态发生变化,Route 的可组合项就会被重新组合。我原以为只有子可组合项会重新组合,因为它是唯一使用状态值的子项?
完整项目是 here,但基本的 Compose 代码如下所示。 (我删除了尽可能多的样板文件)
@Composable
fun AppNav() {
val navController = rememberNavController()
NavHost(navController = navController, startDestination = Screen.Home.route) {
addHome()
}
}
private fun NavGraphBuilder.addHome() {
composable(Screen.Home.route) {
Log.i("Home", "Route Composable")
val homeViewModel = hiltViewModel<HomeViewModel>()
HomeScreen(
homeCount = homeViewModel.homeCount,
updateCount = { homeViewModel.updateCount() },
)
}
}
@Composable
fun HomeScreen(
homeCount: Int,
updateCount: () -> Unit,
) {
Log.i("Home", "Home Screen")
Column(modifier = Modifier.fillMaxSize()) {
Spacer(modifier = Modifier.height(25.dp))
Button(onClick = { updateCount() }) {
Text("Increment count")
}
Spacer(modifier = Modifier.height(25.dp))
Text("Count is: $homeCount")
}
}
我想您需要了解如何在导航到不同路线时使用 saveState
和 restoreState
。
这是一篇关于这件事的文章:
https://developer.android.com/jetpack/compose/navigation#bottom-nav
阅读 documentation more carefully and confirming in the Slack Channel 后,我发现核心问题是对 State 如何与 Compose 一起工作的误解。我在想,它只是在使用状态的地方更改了可组合项,而实际上任何包含(读取)状态的可组合项都会在状态更改时重新组合。我示例中的代码与执行以下操作基本相同:
composable(Screen.Home.route) {
Log.i("Home", "Route Composable")
var homeCount by remember { mutableStateOf(0) }
HomeScreen(
homeCount = homeCount,
updateCount = { homeCount++ },
)
}
无论是像这个版本那样直接在 Composable 中更新状态,还是像原始代码那样在 ViewModel 中更新状态,都没有区别。无论哪种方式,包含状态的可组合项都将进行重组。
Any time a state is updated a recomposition takes place.
和
Any changes to value will schedule recomposition of any composable functions that read value.
TLDR:问题是,当 composable
不依赖于我的 ViewModel 中更新的值时,为什么我在 logcat 中同时看到“可组合路由”和“主屏幕”?我预计只会看到“主屏幕”,因为这是唯一使用来自 ViewModel 的状态值的可组合项。
我最近在生产应用程序中遇到一种情况,我的路由可组合项被调用了两次,导致 UI 出现轻微闪烁。深入研究了几个小时后,我发现闪烁的原因是在为我的撰写屏幕提供 ViewModel 的路由中使用 hiltViewModel
。我进一步挖掘才发现,只要 ViewModel 中的状态发生变化,Route 的可组合项就会被重新组合。我原以为只有子可组合项会重新组合,因为它是唯一使用状态值的子项?
完整项目是 here,但基本的 Compose 代码如下所示。 (我删除了尽可能多的样板文件)
@Composable
fun AppNav() {
val navController = rememberNavController()
NavHost(navController = navController, startDestination = Screen.Home.route) {
addHome()
}
}
private fun NavGraphBuilder.addHome() {
composable(Screen.Home.route) {
Log.i("Home", "Route Composable")
val homeViewModel = hiltViewModel<HomeViewModel>()
HomeScreen(
homeCount = homeViewModel.homeCount,
updateCount = { homeViewModel.updateCount() },
)
}
}
@Composable
fun HomeScreen(
homeCount: Int,
updateCount: () -> Unit,
) {
Log.i("Home", "Home Screen")
Column(modifier = Modifier.fillMaxSize()) {
Spacer(modifier = Modifier.height(25.dp))
Button(onClick = { updateCount() }) {
Text("Increment count")
}
Spacer(modifier = Modifier.height(25.dp))
Text("Count is: $homeCount")
}
}
我想您需要了解如何在导航到不同路线时使用 saveState
和 restoreState
。
这是一篇关于这件事的文章:
https://developer.android.com/jetpack/compose/navigation#bottom-nav
阅读 documentation more carefully and confirming in the Slack Channel 后,我发现核心问题是对 State 如何与 Compose 一起工作的误解。我在想,它只是在使用状态的地方更改了可组合项,而实际上任何包含(读取)状态的可组合项都会在状态更改时重新组合。我示例中的代码与执行以下操作基本相同:
composable(Screen.Home.route) {
Log.i("Home", "Route Composable")
var homeCount by remember { mutableStateOf(0) }
HomeScreen(
homeCount = homeCount,
updateCount = { homeCount++ },
)
}
无论是像这个版本那样直接在 Composable 中更新状态,还是像原始代码那样在 ViewModel 中更新状态,都没有区别。无论哪种方式,包含状态的可组合项都将进行重组。
Any time a state is updated a recomposition takes place.
和
Any changes to value will schedule recomposition of any composable functions that read value.