更改选项卡时,将 NavigationBottomItem 保留在内存中,而不是在 Jetpack Compose 导航中销毁它

J. *_*Doe 5 android kotlin android-jetpack-compose

背景

我正在创建一个聊天应用程序,当用户离开聊天时我需要执行逻辑(聊天只是一个@Composable fun,我正在LocalLifecycleOwner.current与 ViewModel 结合使用,ViewModel 监视onDestroy取消订阅用户的方法)。现在,当用户更改选项卡时也会执行逻辑,因此不应发生这种情况。

问题

我正在使用 aScaffold和 a 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")
    }
}
Run Code Online (Sandbox Code Playgroud)

ian*_*ake 3

您已经有一个保留在内存中的对象 - ViewModel 实例本身。您不应该查看 Lifecycle 实例的销毁,因为当您的应用程序经历配置更改时也会发生这种情况。

相反,您应该查看onClearedViewModel 的方法 - 仅当实例实际从返回堆栈中删除(例如,用户离开聊天)时才会调用该方法。

同样,您不应该使用remember需要保存的状态(同样,remember变量会因多种原因而被擦除,包括当您进行配置更改时)。您应该使用rememberSaveable需要在屏幕保留在返回堆栈上的整个时间内保留的值。