Compose 中的稳定性

Compose 将类型视为稳定或不稳定。如果类型是不可变的,或者 Compose 可以知道其值在重组之间是否发生变化,则该类型是稳定的。如果 Compose 无法知道其值在重组之间是否发生变化,则该类型是不稳定的。

Compose 使用可组合函数参数的稳定性来确定它是否可以跳过重组期间的可组合函数 重组

  • 稳定参数:如果可组合函数具有未更改的稳定参数,Compose 会跳过它。
  • 不稳定参数:如果可组合函数具有不稳定参数,Compose 会在重组组件的父级时始终重组它。

如果您的应用程序包含许多 Compose 始终重组的无用不稳定组件,您可能会观察到性能问题和其他问题。

本文档详细介绍了如何提高应用程序的稳定性以改善性能和整体用户体验。

不可变对象

以下代码片段演示了稳定性和重组背后的通用原则。

Contact 类是一个不可变的数据类。这是因为它的所有参数都是使用 val 关键字定义的基元类型。创建 Contact 实例后,您无法更改对象属性的值。如果您尝试这样做,您将创建一个新对象。

data class Contact(val name: String, val number: String)

ContactRow 可组合函数具有类型为 Contact 的参数。

@Composable
fun ContactRow(contact: Contact, modifier: Modifier = Modifier) {
   var selected by remember { mutableStateOf(false) }

   Row(modifier) {
      ContactDetails(contact)
      ToggleButton(selected, onToggled = { selected = !selected })
   }
}

考虑当用户单击切换按钮且 selected 状态更改时会发生什么。

  1. Compose 评估它是否应该重组 ContactRow 内部的代码。
  2. 它看到 ContactDetails 的唯一参数是类型为 Contact 的参数。
  3. 由于 Contact 是一个不可变的数据类,因此 Compose 可以确定 ContactDetails 的任何参数都没有更改。
  4. 因此,Compose 会跳过 ContactDetails 并且不会重组它。
  5. 另一方面,ToggleButton 的参数已更改,Compose 会重组该组件。

可变对象

虽然前面的示例使用不可变对象,但可以创建可变对象。请考虑以下代码片段。

data class Contact(var name: String, var number: String)

由于 Contact 的每个参数现在都是一个 var,因此该类不再是不可变的。如果其属性发生变化,Compose 将不会意识到。这是因为 Compose 仅跟踪对 Compose 状态对象 的更改。

Compose 认为此类类不稳定。Compose 不会跳过不稳定类的重组。因此,如果 Contact 以这种方式定义,则前面示例中的 ContactRow 将在每次 selected 更改时重组。

Compose 中的实现

考虑 Compose 如何准确地确定在重组期间跳过哪些函数,这可能会有所帮助,但并非至关重要。

当 Compose 编译器运行您的代码时,它会用几个标签之一标记每个函数和类型。这些标签反映了 Compose 在重组期间如何处理函数或类型。

函数

Compose 可以将函数标记为 skippablerestartable。请注意,它可能将函数标记为其中一个、两个或都不标记。

  • 可跳过:如果编译器将可组合函数标记为可跳过,则 Compose 可以在重组期间跳过它,前提是其所有参数与其先前值相等。
  • 可重启:可重启的可组合函数充当“作用域”,重组可以在该作用域中开始。换句话说,该函数可以作为 Compose 在状态更改后可以开始重新执行重组代码的入口点。

类型

Compose 将类型标记为不可变或稳定。每个类型都是其中之一。

  • 不可变:如果类型属性的值永远不会改变且所有方法都是引用透明的,Compose 会将类型标记为不可变。
    • 请注意,所有基元类型都被标记为不可变。这些包括 StringIntFloat
  • 稳定:表示在构造后属性可以更改的类型。如果和当这些属性在运行时更改时,Compose 会意识到这些更改。

调试稳定性

如果您的应用程序正在重组参数未更改的可组合函数,请首先检查其定义以查找明显可变的参数。如果传入具有 var 属性的类型,或者使用已知不稳定类型的 val 属性,Compose 会始终重组组件。

有关如何诊断 Compose 中稳定性的复杂问题的详细信息,请参阅 调试稳定性 指南。

修复稳定性问题

有关如何为 Compose 实现带来稳定性的信息,请参阅 修复稳定性问题 指南。

总结

总的来说,您应该注意以下几点。

  • 参数:Compose 确定可组合函数的每个参数的稳定性,以确定它应该在重组期间跳过哪些可组合函数。
  • 直接修复:如果您注意到您的可组合函数未被跳过并且它会导致性能问题,则应首先检查不稳定性的明显原因,例如 var 参数。
  • 编译器报告:您可以使用 编译器报告 来确定对您的类的稳定性进行了哪些推断。
  • 集合:Compose 始终将集合类视为不稳定,例如 List, SetMap。这是因为无法保证它们是不可变的。您可以使用 Kotlinx 不可变集合 而不是它们,或者将您的类注释为 @Immutable@Stable
  • 其他模块:Compose 始终认为不稳定之处在于它们来自 Compose 编译器未运行的模块。如果需要,请将类包装在 UI 模型类中。

进一步阅读