不练不熟,不写就忘 之 compose 之 remember rememberSaveable rememberSaveableStateHolder
/ 1. 用 @Parcelize 注解让自定义类支持 Parcelable@Parcelize// 2. 直接用 rememberSaveable 保存Text("${// 1. 用 @Parcelize 注解让自定义类支持 Parcelable @Parcelize data class User(val name : String , val age : Int) @Composable f
在 Android Jetpack Compose 中,remember、rememberSaveable、rememberSaveableStateHolder 是三个核心的状态保存 API,用于解决不同场景下的状态持久化问题。它们的核心差异在于状态保存的生命周期范围和适用场景,下面逐一拆解其原理、用法和区别。
一、基础:remember —— 重组间保存状态
核心作用
在 Composable 函数重组(Recomposition)期间保存状态,避免每次重组时重新初始化。
原理
remember会将状态存储在 Composable 的重组作用域中(仅在当前 Composable 及其子 Composable 有效)。- 当 Composable 被销毁(如从屏幕移除、页面退出)时,状态会随之丢失。
- 仅解决「重组时状态重置」问题,不处理「进程存活但页面重建」(如屏幕旋转、配置变更)或「进程杀死」的情况。
用法
@Composable
fun RememberDemo() {
// 保存重组间的状态(屏幕旋转后会重置)
var count by remember { mutableStateOf(0) }
Button(onClick = { count++ }) {
Text("点击次数:$count")
}
}
关键特点
- 生命周期:与 Composable 实例绑定(重组存活,销毁消失)。
- 适用场景:临时状态(如输入框临时输入、列表展开状态),无需跨配置变更保存。
- 限制:无法应对屏幕旋转、Activity 重建,更不能应对进程杀死。
二、进阶:rememberSaveable —— 跨配置变更+进程恢复保存状态
核心作用
在 remember 的基础上,额外支持跨配置变更(如屏幕旋转、语言切换) 和进程被杀死后恢复状态。
原理
- 本质是
remember+SavedStateHandle(Jetpack 提供的状态持久化工具)。 - 状态会被存储在:
- 配置变更时:存储在
Activity/Fragment的SavedInstanceState中(内存级)。 - 进程杀死时:若启用了「自动保存」(默认开启),会持久化到磁盘(需状态支持序列化)。
- 配置变更时:存储在
- 支持的数据类型:
- 基础类型(Int、String、Boolean 等)、Parcelable、Serializable 自动支持。
- 自定义类型需通过
saveable注册序列化器(如@Parcelize注解)。
用法
1. 基础类型(自动支持)
@Composable
fun RememberSaveableDemo() {
// 屏幕旋转、进程恢复后,count 会保留
var count by rememberSaveable { mutableStateOf(0) }
Button(onClick = { count++ }) {
Text("点击次数:$count")
}
}
2. 自定义类型(需序列化)
// 1. 用 @Parcelize 注解让自定义类支持 Parcelable
@Parcelize
data class User(val name: String, val age: Int)
@Composable
fun CustomTypeSaveableDemo() {
// 2. 直接用 rememberSaveable 保存
var user by rememberSaveable { mutableStateOf(User("Tom", 20)) }
Button(onClick = { user = user.copy(age = user.age + 1) }) {
Text("${user.name} 年龄:${user.age}")
}
}
3. 复杂类型(自定义保存/恢复逻辑)
若类型不支持 Parcelable/Serializable,可通过 save 和 restore 回调自定义逻辑:
@Composable
fun ComplexTypeSaveableDemo() {
var date by rememberSaveable(
stateSaver = object : Saver<LocalDate, Long> {
override fun save(value: LocalDate): Long = value.toEpochDay()
override fun restore(value: Long): LocalDate = LocalDate.ofEpochDay(value)
}
) { mutableStateOf(LocalDate.now()) }
Button(onClick = { date = date.plusDays(1) }) {
Text("当前日期:$date")
}
}
关键特点
- 生命周期:与
Activity/Fragment或NavBackStackEntry(导航组件)绑定(配置变更存活,进程杀死可恢复)。 - 适用场景:需要跨屏幕旋转、页面重建保留的状态(如表单输入、用户选择、分页页码)。
- 限制:
- 状态必须可序列化(或自定义 Saver)。
- 不适合保存大量数据(如图片、大列表),建议用 Room 或 DataStore。
- 若 Composable 是临时弹出(如 Dialog),关闭后状态会丢失(需结合
rememberSaveableStateHolder)。
三、高阶:rememberSaveableStateHolder —— 临时组件的状态持久化
核心作用
为「临时存在的 Composable」(如 Tab 页、Dialog、导航返回栈中的页面)保存状态,即使组件被销毁(如 Tab 切换时隐藏),重新显示时也能恢复状态。
背景
rememberSaveable 仅在组件「存活但重组」或「配置变更」时有效。若组件被完全移除(如 Tab 切换时销毁前一个 Tab 的 Composable),rememberSaveable 存储的状态会丢失。此时需要 rememberSaveableStateHolder 手动管理状态的「保存」和「恢复」。
原理
- 本质是一个「状态容器」,通过唯一 key 关联每个临时组件的状态。
- 当组件被销毁时,调用
saveState(key)将状态保存到容器中。 - 当组件重新创建时,调用
restoreState(key)从容器中恢复状态。 - 状态的持久化能力继承自
rememberSaveable(支持跨配置变更和进程恢复)。
用法(典型场景:Tab 切换状态保存)
@Composable
fun TabStateHolderDemo() {
// 1. 创建 SaveableStateHolder(生命周期与父 Composable 绑定)
val stateHolder = rememberSaveableStateHolder()
// 2. Tab 切换状态
var selectedTabIndex by remember { mutableStateOf(0) }
val tabs = listOf("Tab1", "Tab2", "Tab3")
Column {
TabRow(selectedTabIndex = selectedTabIndex) {
tabs.forEachIndexed { index, title ->
Tab(
selected = selectedTabIndex == index,
onClick = { selectedTabIndex = index },
text = { Text(title) }
)
}
}
// 3. 用 stateHolder 包裹每个 Tab 的内容,通过唯一 key 关联
stateHolder.SaveableStateProvider(key = selectedTabIndex) {
when (selectedTabIndex) {
0 -> TabContent(tabName = "Tab1")
1 -> TabContent(tabName = "Tab2")
2 -> TabContent(tabName = "Tab3")
}
}
}
}
// 每个 Tab 的内容(含需要保存的状态)
@Composable
fun TabContent(tabName: String) {
// 这里的 rememberSaveable 状态会被 stateHolder 管理
var count by rememberSaveable { mutableStateOf(0) }
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Text("$tabName 点击次数:$count")
Button(onClick = { count++ }) {
Text("点击")
}
}
}
关键用法说明
SaveableStateProvider(key: Any):包裹需要保存状态的 Composable,key必须唯一(如 Tab 索引、页面 ID)。- 子 Composable 中使用
rememberSaveable定义的状态,会自动被stateHolder捕获和管理。 - 即使子 Composable 被销毁(如 Tab 切换时),状态会通过
key存储在stateHolder中,重新切换回来时自动恢复。
其他场景
- 导航组件(NavHost):默认会为每个
NavBackStackEntry自动管理状态,但若需要自定义(如手动弹出页面后恢复),可结合rememberSaveableStateHolder。 - Dialog/BottomSheet:关闭后重新打开需恢复状态时,用
stateHolder包裹 Dialog 内容。
关键特点
- 生命周期:与
stateHolder的创建者(父 Composable)绑定(父组件存活则状态保留)。 - 适用场景:临时组件(Tab、Dialog、动态页面)的状态持久化,避免切换/关闭后状态丢失。
- 依赖:子组件的状态必须通过
rememberSaveable定义(remember状态不会被保存)。
四、三者核心区别对比
| 特性 | remember |
rememberSaveable |
rememberSaveableStateHolder |
|---|---|---|---|
| 核心作用 | 重组间保状态 | 跨配置变更+进程恢复保状态 | 临时组件(销毁后)保状态 |
| 生命周期绑定 | 当前 Composable 实例 | Activity/Fragment/NavBackStackEntry | 父 Composable(创建者) |
| 状态丢失场景 | Composable 销毁、配置变更 | 进程杀死(未序列化)、组件销毁 | 父 Composable 销毁、进程杀死(未序列化) |
| 序列化要求 | 无 | 是(或自定义 Saver) | 是(依赖子组件 rememberSaveable) |
| 适用场景 | 临时状态(如列表展开) | 跨旋转/重建的状态(如表单输入) | Tab/Dialog/动态页面(切换恢复) |
| 状态管理方式 | 自动(重组时保留) | 自动(系统恢复) | 手动(通过 key 关联) |
五、使用建议
- 若仅需「重组时不重置」:用
remember(如临时变量、UI 状态切换)。 - 若需「跨屏幕旋转/页面重建保留」:用
rememberSaveable(如用户输入、分页信息)。 - 若需「临时组件(Tab/Dialog)销毁后恢复状态」:用
rememberSaveableStateHolder+ 子组件rememberSaveable。 - 避免过度使用
rememberSaveable:大量/复杂数据用 Room/DataStore 持久化,rememberSaveable仅存轻量状态。 - 自定义类型优先用
@Parcelize:简单高效,无需手动写序列化逻辑。
通过三者的组合,可以覆盖 Compose 中绝大多数状态持久化场景,确保 UI 状态的一致性和用户体验。
更多推荐


所有评论(0)