6、提升状态的场景

6 阅读4分钟

Jetpack Compose 提升状态的场景

在 Jetpack Compose 中,提升状态是管理复杂界面和业务逻辑的重要手段。它通过将状态从可组合项中提取出来,移至更高的层级或专门的逻辑处理类中,以实现状态共享、逻辑解耦和界面可重用性。以下是 Jetpack Compose 中提升状态的详细场景说明:


1. 状态提升的基本概念

状态提升的定义

  • 状态提升是指将界面状态从可组合项内部移到其调用方或更高层级的逻辑处理类中,使可组合项变为无状态。这有助于实现状态共享、提高可组合项的重用性,并简化测试。

最佳实践

  • 将状态提升至读取和写入该状态的所有可组合项的最低共同祖先实体。
  • 使状态尽可能靠近其使用位置,同时考虑界面逻辑和业务逻辑的需要。

2. 界面状态和逻辑的类型

界面状态

  • 屏幕界面状态:需要在屏幕上显示的内容,如新闻报道列表。
  • 界面元素状态:界面元素的固有属性,如显示/隐藏状态、字体、字号或颜色。

逻辑

  • 业务逻辑:决定应用数据的产品要求实现,如将书签保存到数据库。
  • 界面逻辑:决定如何在屏幕上显示界面状态,如根据用户选择类别获取搜索栏提示。

3. 提升状态的场景

场景 1:界面逻辑需要

适用情况

  • 当界面逻辑需要读取或写入状态,且该状态需要在多个可组合项之间共享时。

实现方式

  • 以可组合项作为状态所有者
    • 如果状态和逻辑较简单,可以在可组合项中直接管理状态。
    • 示例:一个可展开/收起的聊天气泡,其状态(showDetails)仅在该可组合项内使用。
@Composable
fun ChatBubble(message: Message) {
    var showDetails by rememberSaveable { mutableStateOf(false) }
    Text(
        text = AnnotatedString(message.content),
        modifier = Modifier.clickable { showDetails = !showDetails }
    )
    if (showDetails) {
        Text(message.timestamp)
    }
}
  • 在可组合项中提升状态
    • 当多个可组合项需要共享状态时,将状态提升至它们的最低共同祖先。
    • 示例:聊天应用中的 LazyColumn 状态提升,以便在多个位置控制滚动。
@Composable
fun ConversationScreen() {
    val lazyListState = rememberLazyListState()
    MessagesList(messages, lazyListState)
    UserInput(onMessageSent = { scope.launch { lazyListState.scrollToItem(0) } })
}

@Composable
fun MessagesList(messages: List<Message>, lazyListState: LazyListState) {
    LazyColumn(state = lazyListState) {
        items(messages) { message -> Message(message) }
    }
}

场景 2:业务逻辑需要

适用情况

  • 当业务逻辑需要访问或修改状态,且该状态需要在多个界面或业务层之间共享时。

实现方式

  • 以普通状态容器类作为状态所有者
    • 对于复杂的界面逻辑,将状态和逻辑委托给普通状态容器类。
    • 示例:使用 LazyListState 控制 LazyColumn 的滚动位置。
@Stable
class LazyListState {
    private val scrollPosition = LazyListScrollPosition()
    suspend fun scrollToItem(index: Int) { /* ... */ }
}
  • 以 ViewModel 作为状态所有者
    • 当业务逻辑位于层次结构的其他层(如业务层和数据层)时,使用 ViewModel 提供对业务逻辑的访问权限,并准备要在屏幕上呈现的应用数据。
    • 示例:聊天应用的 ConversationViewModel 管理消息列表和发送消息的逻辑。
class ConversationViewModel(channelId: String, messagesRepository: MessagesRepository) : ViewModel() {
    val messages = messagesRepository.getLatestMessages(channelId).stateIn(
        scope = viewModelScope,
        started = SharingStarted.WhileSubscribed(5_000),
        initialValue = emptyList()
    )
    fun sendMessage(message: Message) { /* ... */ }
}

4. 状态提升的优势

  • 单一可信来源:确保状态只有一个来源,避免数据不一致。
  • 封装性:只有状态所有者能够修改状态,提高代码的可维护性。
  • 可共享性:多个可组合项可以共享同一个状态,提高代码的重用性。
  • 可拦截性:调用方可以在状态更新前进行拦截或修改,增强灵活性。
  • 解耦性:将状态管理与界面展示分离,降低代码的复杂性。

5. 注意事项

  • 避免过度提升:仅在必要时提升状态,避免将状态传递给不需要它的可组合项。
  • 性能考虑:对于频繁更新的状态,考虑使用 derivedStateOf 减少不必要的重组。
  • 协程作用域:在调用从 Compose 界面元素状态公开的挂起函数时,确保使用作用域限定于组合的协程,避免运行时异常。

总结

Jetpack Compose 中的状态提升是一种强大的模式,它通过将状态从可组合项中提取出来,移至更高的层级或专门的逻辑处理类中,实现了状态共享、逻辑解耦和界面可重用性。根据界面逻辑和业务逻辑的需要,合理选择状态提升的方式(如可组合项、普通状态容器类或 ViewModel),可以构建出高效、可维护和响应迅速的 Android 应用界面。