7、保存界面状态

保存界面状态

在 Jetpack Compose 应用中,正确保存和恢复界面状态对于提供良好的用户体验至关重要。当应用因重新创建 activity 或进程而丢失界面状态时,用户可能会遇到数据丢失或界面不一致的问题。本指南将详细介绍如何在 Jetpack Compose 中保存和恢复界面状态。


1. 界面状态丢失的原因

Android 应用可能会因以下事件而丢失界面状态:

  • 系统发起的进程终止:如内存不足时系统杀掉进程。
  • 配置更改:如屏幕旋转、语言切换等。
  • 用户发起的进程终止:如用户显式关闭应用。

对于用户发起的进程终止,瞬时状态的丢失通常是合理的。但在其他情况下,保留状态对于提供流畅的用户体验至关重要。


2. 保存界面状态的策略

根据状态提升的位置和所需的逻辑,可以使用不同的 API 来保存和恢复界面状态。

2.1 界面逻辑中的状态保存

当状态在界面逻辑中提升时(无论是在可组合函数还是作用域限定为组合的普通状态容器类中),可以使用 rememberSaveable 在重新创建 activity 和进程之后保留状态。

示例:保存单个布尔值界面元素状态

kotlin 复制代码
@Composable
fun ChatBubble(message: Message) {
    var showDetails by rememberSaveable { mutableStateOf(false) }
    ClickableText(
        text = AnnotatedString(message.content),
        onClick = { showDetails = !showDetails }
    )
    if (showDetails) {
        Text(message.timestamp)
    }
}

存储机制

  • rememberSaveable 通过保存的实例状态机制将界面元素状态存储在 Bundle 中。
  • 它能够自动将基元类型存储到 Bundle 中。对于非基元类型(如数据类),可以使用 Parcelize 注解、listSavermapSaver 等 Compose API,或实现自定义的 Saver 类。

示例 :保存 LazyListState

kotlin 复制代码
@Composable
fun rememberLazyListState(
    initialFirstVisibleItemIndex: Int = 0,
    initialFirstVisibleItemScrollOffset: Int = 0
): LazyListState {
    return rememberSaveable(saver = LazyListState.Saver) {
        LazyListState(initialFirstVisibleItemIndex, initialFirstVisibleItemScrollOffset)
    }
}

2.2 业务逻辑中的状态保存

当界面元素状态因业务逻辑需要被提升到 ViewModel 时,可以使用 ViewModel 的 API 来保存状态。

ViewModel 的优势

  • ViewModel 实例在配置更改(如屏幕旋转)后仍然存在,因此提升到 ViewModel 的界面状态会保留在内存中。
  • 但 ViewModel 实例在系统发起的进程终止后将失效。为了持久化状态,可以使用 SavedStateHandle

示例 :使用 SavedStateHandle 保存界面元素状态

kotlin 复制代码
class ConversationViewModel(savedStateHandle: SavedStateHandle) : ViewModel() {
    var message by savedStateHandle.saveable(stateSaver = TextFieldValue.Saver) {
        mutableStateOf(TextFieldValue(""))
    }
    private set

    fun update(newMessage: TextFieldValue) {
        message = newMessage
    }
}

@Composable
fun UserInput(viewModel: ConversationViewModel = viewModel()) {
    TextField(
        value = viewModel.message,
        onValueChange = { viewModel.update(it) }
    )
}

SavedStateHandle 的 API

  • saveable API :以 MutableState 形式读取和写入界面元素状态,支持基元类型和自定义 Saver
  • getStateFlow API :存储界面元素状态,并将其用作来自 SavedStateHandle 的数据流。

3. 最佳实践

3.1 界面逻辑中的最佳实践

  • 避免存储大型对象rememberSaveable 使用 Bundle 存储界面状态,Bundle 大小有限。存储大型复杂对象或对象列表可能会导致 TransactionTooLargeException
  • 存储最少必需状态:仅存储恢复界面状态所需的最少数据(如 ID 或键),并使用这些数据从数据层重新生成复杂状态。

3.2 业务逻辑中的最佳实践

  • 使用 SavedStateHandle 存储简单状态SavedStateHandle 也使用 Bundle 机制,因此应仅用于存储简单的界面元素状态。
  • 复杂状态使用永久性存储 :屏幕界面状态可能非常复杂且庞大,不应存储在 SavedStateHandle 中。可以使用本地永久性存储(如 Room 数据库)来存储复杂数据。

4. 验证状态恢复

为了验证重新创建 activity 或进程后,Compose 元素中使用 rememberSaveable 存储的状态是否已正确恢复,可以使用 StateRestorationTester

示例 :使用 StateRestorationTester 验证状态恢复

kotlin 复制代码
@Test
fun testStateRestoration() {
    val tester = StateRestorationTester(context)
    tester.setContent {
        val (value, setValue) = rememberSaveable { mutableStateOf(0) }
        // Test logic here
    }
}

5. 总结

  • 界面逻辑中的状态 :使用 rememberSaveable 在重新创建 activity 和进程后保留状态。
  • 业务逻辑中的状态 :如果状态被提升到 ViewModel,使用 SavedStateHandle 保存状态。
  • 最佳实践:避免在 Bundle 中存储大型复杂对象,存储最少必需状态,并使用永久性存储处理复杂数据。

通过合理使用这些 API 和最佳实践,可以确保 Jetpack Compose 应用在面对配置更改和进程终止时保持一致的用户体验。

相关推荐
bqliang1 小时前
从喝水到学会 Android ASM 插桩
android·kotlin·android studio
S***q19215 小时前
Kotlin内联函数优化
android·开发语言·kotlin
小墙程序员15 小时前
在Android中,kotlin 的一些开发技巧(二)
android·kotlin
u***u68517 小时前
Kotlin多平台开发实践
android·开发语言·kotlin
Q***K5517 小时前
Kotlin与Java互操作指南
java·开发语言·kotlin
Q***f6351 天前
Kotlin在Android性能优化中的工具
android·开发语言·kotlin
Chrison_mu2 天前
Android项目背景动效-Kotlin
android·开发语言·kotlin
啃火龙果的兔子2 天前
如何控制kotlin项目back的时候,只回退webview的路由
开发语言·kotlin·harmonyos
_BugMaker2 天前
重学Kotlin(四)面向对象
kotlin