remember()、rememberSaveable()和rememberSaveableStateHolder()
从字面上理解,这三个函数都是对Composable重组时的数据做记忆保存的,但是它们也有一些区别。
1.remember
remember: 它的作用是在重组 (Recomposition) 期间保持状态。
但如果Composable从组件树中被移除(例如,导航到新页面),或者发生配置变更(如屏幕旋转)导致Activity重建,它的状态就会丢失。
remember可以带多个参数一个lambda,形式为remember(key1,key2...){ ...... },在第一次的时候会执行lambda,后面需要key改变的时候,才会执行lambda。
我们常见的remember{......},可以理解为key始终不变的情况,也就是只在第一次重组的时候执行一次,后面不会再执行。
2.rememberSaveable
rememberSaveable是remember的增强版,不仅能在重组期间保持状态,还能在配置变更甚至进程死亡后存活下来。它通过将状态保存到Bundle中来实现这一点。
但是,rememberSaveable有一个前提:它所包裹的Composable必须持续存在于组件树中。一旦Composable被移除,其状态也会被丢弃。
这时,rememberSaveableStateHolder就派上了用场。它解决了当Composable暂时离开组件树时,如何保留其rememberSaveable状态的问题。
3.rememberSaveableStateHolder
rememberSaveableStateHolder() 是一个函数,会返回一个SaveableStateHolder类型的对象,顾名思义,就是一个存储rememberSaveable的容器,当页面重新切回来加入到组件树之后,rememberSaveable的内容还在。
工作流程如下:
- 创建持有者: 在这些动态Composable的共同父级中,调用
rememberSaveableStateHolder()来创建一个实例。 - 提供状态: 将每个动态的Composable内容用
saveableStateHolder.SaveableStateProvider(key = ...)包裹起来。这个key必须能唯一标识该部分内容。 - 保存状态: 当一个
SaveableStateProvider及其内容因为不再被显示而离开组件树时,SaveableStateHolder会自动保存其内部所有由rememberSaveable定义的状态,并将这些状态与你提供的key关联起来。 - 恢复状态: 当你再次使用同一个key来组合
SaveableStateProvider时,SaveableStateHolder会找到之前保存的状态并恢复它。 这确保了用户返回该界面时,能看到离开前的样子,比如滚动列表的位置或文本框中输入的内容。
下面举一个例子,在Main主屏幕中,存在4个子页面 HomeScreen()、NavScreen()、ProjectScreen()、MyScreen(),这4个页面在切换的时候,由于被saveableStateHolder.SaveableStateProvider包裹,所以会保存状态。
@Composable
fun MainScreen(){val saveableStateHolder = rememberSaveableStateHolder()Column(modifier = Modifier.padding(innerPadding)) {when (navIndex) {0 -> saveableStateHolder.SaveableStateProvider(navItems[0].label) {HomeScreen()}1 -> saveableStateHolder.SaveableStateProvider(navItems[1].label) {NavScreen()}2 -> saveableStateHolder.SaveableStateProvider(navItems[2].label) {ProjectScreen()}3 -> saveableStateHolder.SaveableStateProvider(navItems[3].label) {MyScreen()}}}
如果没有rememberSaveableStateHolder,每次切换回之前的屏幕或标签页时,其状态都会被重置,导致糟糕的用户体验。
4.主要应用场景
rememberSaveableStateHolder在以下场景中扮演着至关重要的角色:
- 自定义导航: 在不使用官方
NavHost,而是手动管理导航逻辑时,rememberSaveableStateHolder是实现页面状态保持的核心工具。 即使用户在多个屏幕之间来回跳转,每个屏幕的滚动位置等UI状态也能被完整保留。(也就是说NavHost内部自动实现了rememberSaveableStateHolder机制) - 带标签的界面 (Tabbed Layouts): 当用户在不同的标签页之间切换时,只有当前激活的标签页内容在组件树中。rememberSaveableStateHolder可以确保用户切换回之前的标签页时,其状态(如列表滚动位置)不会丢失。
这里要说明一下,HorizontalPager内部也是默认实现了rememberSaveableStateHolder机制的,所以在不同Pager直接切换,Pager的rememberSaveable状态也是会有保留的。
5.SaveableStateHolder缓存内容的移除
SaveableStateHolder 中保存的内容会在以下时刻被移除:
- 手动移除: 通过显式调用
holder.removeState(key)。这是最精确的控制方式,适用于动态增删状态的场景。 - 自动销毁: 当创建 SaveableStateHolder 的那个 Composable 自身被永久性地从组合树中移除时,holder 实例被垃圾回收,其内部保存的所有状态都会被清除。
说得直白点,就是创建SaveableStateHolder的那个页面被NavHost出栈了,没有地方对它引用了,自然就会释放。
6.注意事项
如果使用不当,rememberSaveableStateHolder确实有可能导致内存占用过大,甚至在极端情况下增加内存溢出(OOM)的风险。
rememberSaveableStateHolder是一个强大的工具,但能力越大,责任也越大。它通过牺牲一定的内存占用换来了更好的用户体验(即时状态恢复)。这种权衡在大多数情况下是值得的,但前提是开发者必须主动管理其持有的状态生命周期,通过适时调用removeState()来防止内存无限增长。
