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。请注意,它可能会将函数标记为其中一个、两个或都不是。

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

类型

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

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

调试稳定性

如果你的应用正在重新组合参数未更改的可组合项,请首先检查其定义中明显可变的参数。如果你传入具有 var 属性或使用已知不稳定类型的 val 属性的类型,Compose将始终重新组合组件。

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

修复稳定性问题

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

总结

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

  • 参数:Compose确定可组合项的每个参数的稳定性,以确定在重新组合期间应跳过哪些可组合项。
  • 立即修复:如果你注意到你的可组合项没有被跳过 *并且它导致性能问题*,你应该首先检查不稳定性的明显原因,例如 var 参数。
  • 编译器报告:你可以使用编译器报告来确定正在推断关于你的类的哪些稳定性。
  • 集合:Compose总是认为集合类是不稳定的,例如 List、SetMap。这是因为无法保证它们是不可变的。你可以改用Kotlinx不可变集合,或者将你的类注释为 @Immutable@Stable
  • 其他模块:Compose总是认为来自Compose编译器未运行的模块是不稳定的。如果需要,请将类包装在UI模型类中。

进一步阅读