前言
上一课学习了 if 条件渲染和 LazyColumn 懒加载列表,掌握动态UI与长列表渲染。
我们一直用 remember 保存状态,但它有一个致命缺陷:手机旋转屏幕、系统因内存不足重建Activity时,remember保存的数据会全部丢失,重置为初始值。
本课学习 rememberSaveable ,它能在系统重建(配置变更、进程重启)时自动保存和恢复状态,是真实安卓开发必须掌握的知识点。
前置:完成1~7课,掌握remember、状态、基础布局与列表
一、完整可运行示例源码
packagecom.example.composefirstimportandroid.os.Bundleimportandroidx.activity.ComponentActivityimportandroidx.activity.compose.setContentimportandroidx.compose.foundation.layout.Columnimportandroidx.compose.foundation.layout.Spacerimportandroidx.compose.foundation.layout.heightimportandroidx.compose.foundation.layout.paddingimportandroidx.compose.material3.Buttonimportandroidx.compose.material3.Textimportandroidx.compose.runtime.Composableimportandroidx.compose.runtime.getValueimportandroidx.compose.runtime.mutableIntStateOfimportandroidx.compose.runtime.saveable.rememberSaveableimportandroidx.compose.runtime.setValueimportandroidx.compose.ui.Modifierimportandroidx.compose.ui.tooling.preview.Previewimportandroidx.compose.ui.unit.dpclassMainActivity:ComponentActivity(){overridefunonCreate(savedInstanceState:Bundle?){super.onCreate(savedInstanceState)setContent{SaveableDemoPage()}}}@ComposablefunSaveableDemoPage(){// rememberSaveable:系统重建时自动保存恢复varcountbyrememberSaveable{mutableIntStateOf(0)}Column(modifier=Modifier.padding(20.dp)){Text(text="当前计数:$count")Spacer(modifier=Modifier.height(12.dp))Button(onClick={count++}){Text("+1")}Spacer(modifier=Modifier.height(8.dp))Button(onClick={count=0}){Text("重置")}}}@Preview@ComposablefunSaveablePreview(){SaveableDemoPage()}二、逐行代码解读
- rememberSaveable { mutableIntStateOf(0) }
和 remember 用法几乎一模一样,唯一区别:数据会被序列化保存到Bundle中,Activity因配置变更、系统重建的时候,可以自动恢复数据。
导入路径: androidx.compose.runtime.saveable.rememberSaveable - remember 丢失数据的场景
旋转屏幕、切换系统深色模式、修改系统字体大小、切换系统语言,这些操作都会触发安卓配置变更,销毁并且重建Activity。
remember仅仅保存在运行时内存当中,页面重建之后内存清空,状态直接归零。 - rememberSaveable 底层原理
封装安卓原生 onSaveInstanceState 保存机制,把状态序列化放进Bundle,页面重建之后,从Bundle读取数据恢复状态。
存在功能限制:只能存储可以序列化的数据。整型、字符串、布尔值等基础类型开箱即用;自定义实体类需要实现Parcelable,或者手动提供Saver保存器。 - 简单区分 rememberSaveable 和 ViewModel
rememberSaveable 适合少量UI状态,开箱即用,代码简短。
ViewModel 适合大批量业务数据、跨页面共享数据,生命周期更长,旋转屏幕不会丢失。ViewModel属于架构进阶内容,后续课程再详细讲解。
三、核心概念文字对比
remember:重组的时候可以保留状态;屏幕旋转、页面重建数据丢失;数据类型没有限制;适合页面临时状态。
rememberSaveable:重组的时候可以保留状态;屏幕旋转、页面重建自动恢复;数据需要支持序列化;适合表单、计数器等需要防丢失的数据。
记忆口诀:临时状态用remember,怕丢数据用rememberSaveable。
四、综合练习题
填空题
- 能够在旋转屏幕、系统重建时自动保存恢复状态的函数是________。
- rememberSaveable 底层基于安卓传统的________机制,把数据存入Bundle。
- rememberSaveable 要求保存的数据类型必须是________的,自定义类需要实现Parcelable。
答案与解析
- rememberSaveable ;解析:专门用于跨配置变更、系统重建保存状态。
- onSaveInstanceState ;解析:安卓传统保存实例状态机制,Compose封装后自动调用。
- 可序列化;解析:Bundle只能存可序列化数据,基础类型默认支持,自定义类需Parcelable。
判断题
- remember 和 rememberSaveable 在普通重组时都能保留数据,效果一样。()
答案:正确。理由:两者在Compose重组时都会保存数据,区别只在系统重建场景。
- 用remember保存的计数器,旋转屏幕后数值会重置为初始值。()
答案:正确。理由:remember只存在内存中,Activity销毁重建后内存数据清空。
- rememberSaveable 可以保存任意类型的数据,包括不可序列化的对象。()
答案:错误。理由:rememberSaveable基于Bundle存储,数据必须可序列化,不可序列化对象会运行时报错。
简答题
- 什么场景下应该使用rememberSaveable而不是remember?举两个实际例子。
参考答案:
当状态需要在系统重建(旋转屏幕、语言切换、深色模式切换、内存回收重建)后保留时使用。
例子1:用户在表单页输入了一半内容,旋转屏幕后不能清空;
例子2:计数器页面,用户点了很多次,旋转屏幕后数值要保留。
代码实操题
把第7课的待办列表示例改造:输入文本和待办列表都使用rememberSaveable保存,确保旋转屏幕后数据不丢失。写出关键状态定义代码。参考答案
@ComposablefunTodoListPage(){varinputTextbyrememberSaveable{mutableStateOf("")}vartodoListbyrememberSaveable{mutableStateOf(listOf<String>())}funaddTodo(item:String){todoList=todoList+item}funremoveTodo(item:String){todoList=todoList-item}// ... 其余UI代码和第7课一致}解析:
inputText是字符串,默认可序列化,直接使用rememberSaveable。
todoList采用不可变List,增删时生成全新集合进行赋值,正常触发重组。
注意:mutableStateListOf直接套rememberSaveable存在兼容隐患,练习场景优先选用不可变列表。
五、本课小结
- remember 仅重组保数据,屏幕旋转、页面重建数据丢失。
- rememberSaveable 依托安卓实例保存机制,系统重建自动恢复状态。
- 存入的数据需要支持序列化,自定义对象需要额外实现Parcelable。
- 选型参考:临时界面状态用remember,需要防丢失的数据选用rememberSaveable。
下一课:SideEffect副作用 + LaunchedEffect,处理异步任务和生命周期。