bounty还有4天到期。回答此问题可获得+100声望奖励。J. Doe正在寻找一个答案从一个有信誉的来源。
后台
我正在创建一个聊天应用程序,当用户离开聊天时,我需要执行逻辑(聊天只是一个@Composable fun
,我正在使用LocalLifecycleOwner.current
与ViewModel的组合,它监视onDestroy
方法以取消订阅用户)。既然当用户更改选项卡时也会执行逻辑,那么这种情况就不应该发生了。
问题
我用的是Scaffold
和BottomNavigation
。当我切换选项卡时,旧的选项卡将被销毁。我不希望出现这种情况,旧选项卡应该保留在内存中。remember
块在返回选项卡时也会重新执行,我不希望这样。我应该使用多个导航主机还是什么?
目标
在没有重新执行remember
块的情况下在选项卡之间导航(并且LocalLifecycleOwner.current
不应该发布onDestroy
)。
示例代码
整个项目可以在这里找到:https://github.com/Jasperav/JetpackComposeNavigation。您可以看到,当切换选项卡时,remember
块被重新执行,VM
被销毁(参见日志记录)。我不想要这种行为,它应该被保存在内存中。这是相关代码:
@Composable
fun Screen() {
val items = listOf(
Triple("a", Icons.Default.Person, Icons.Filled.Person),
Triple("b", Icons.Default.Notifications, Icons.Filled.Notifications),
)
var selectedTab = items[0]
val navHostController = rememberNavController()
Scaffold(
bottomBar = {
BottomNavigation {
items.forEachIndexed { index, item ->
val isSelected = index == items.indexOf(selectedTab)
BottomNavigationItem(
icon = { Icon(if (isSelected) item.second else item.third, contentDescription = null) },
label = { Text(text = item.first) },
selected = isSelected,
onClick = {
navHostController.navigate(item.first) {
popUpTo(navHostController.graph.findStartDestination().id)
launchSingleTop = true
}
}
)
}
}
}
) {
NavHost(
navHostController,
startDestination = items[0].first,
Modifier.padding(it)
) {
composable(items[0].first) {
selectedTab = items[0]
val lifecycle = LocalLifecycleOwner.current
val viewModel: ModelDontDestory = viewModel(factory = viewModelFactory {
ModelDontDestory(lifecycle)
})
remember {
println("Recomposed first")
""
}
Text("first")
}
composable(items[1].first) {
selectedTab = items[1]
val lifecycle = LocalLifecycleOwner.current
val viewModel: ModelDontDestory = viewModel(factory = viewModelFactory {
ModelDontDestory(lifecycle)
})
remember {
println("Recomposed second")
""
}
Text("Second")
}
}
}
}
inline fun <VM : ViewModel> viewModelFactory(crossinline f: () -> VM) =
object : ViewModelProvider.Factory {
override fun <T : ViewModel> create(aClass: Class<T>): T = f() as T
}
class ModelDontDestory(val lifecycle: LifecycleOwner): ViewModel(), DefaultLifecycleObserver {
init {
lifecycle.lifecycle.addObserver(this)
}
override fun onDestroy(owner: LifecycleOwner) {
super.onDestroy(owner)
println("This should never happen, this should be kept in memory")
}
}
2条答案
按热度按时间c3frrgcw1#
您已经有一个对象保留在内存中-ViewModel示例本身。您不应该查看Lifecycle示例的销毁,因为当您的应用程序经历配置更改时也会发生这种情况。
相反,您应该查看ViewModel的
onCleared
方法--只有当示例实际从回退堆栈中删除时(例如,用户离开聊天),才会调用该方法。类似地,您不应该将
remember
用于需要保存的状态(同样,remember
变量会由于多种原因而被擦除,包括在您进行配置更改时)。对于需要在屏幕保留在返回堆栈上的整个时间内保留的值,应该使用rememberSaveable
。zbq4xfa02#
根据我对“Lifecycle of composables”的理解以及Ziv Kesten的“Implement Bottom Bar Navigation in Jetpack Compose”示例,
remember
块(explained here)和生命周期事件与@Composable
函数的生命周期有着内在的联系。当您离开屏幕时,它的@Composable
函数将被丢弃,当您返回时,它们将被重新组合。在Jetpack Compose中,
remember
函数用于在重组过程中保留状态。但是,使用
remember
保存的状态无法在配置更改或进程死亡后继续存在。如果您需要在配置更改或进程死亡时保留状态,则应该使用rememberSaveable
函数。要使用
rememberSaveable
,您需要确保您保存的数据是可序列化的,因为它需要放入Bundle
。如:
MyState
需要是一个属性都是可序列化的数据类。rememberSaveable
函数确保在配置更改和进程死亡时保存和恢复状态。然而,在您提供的代码中,似乎您正在使用
remember
来在重新组合可组合对象时触发println
。remember
(和rememberSaveable
)通常不用于此目的。相反,remember
和rememberSaveable
用于在重组或配置更改时保留状态。如果您只想在重新组合组合时打印日志消息,则可以直接使用
println
,而不使用remember
。如果你想在内存中保存一些状态,你可以同时使用
ViewModel
和rememberSaveable
,其中rememberSaveable
保存UI状态,ViewModel
保存更持久的数据状态。这意味着这将是一个更简单的实现:
随着变化:
1.已从
ViewModel
中删除LifecycleOwner
。ViewModel
不需要引用LifecycleOwner
。相反,当不再需要ViewModel
时,应该使用ViewModel
的onCleared
方法来执行清理。1.已将所选选项卡索引的
remember
更改为rememberSaveable
。这确保了即使发生配置更改(例如,屏幕旋转)时也能记住所选选项卡。1.删除了在重新组合组合组件时用于打印日志消息的
remember
块。如果您需要在重新组合Composable时执行某个操作,则可以直接在Composable中调用该操作。remember
函数不需要这样做。