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 State 对象的变化。

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 模型类中。

拓展阅读