强跳过模式

强跳过(Strong Skipping)是 Compose 编译器中提供的一种模式。启用后,它会以两种方式改变编译器的行为:

  • 具有不稳定参数的可组合项变得可跳过
  • 具有不稳定捕获的 Lambda 会被记住

启用强跳过模式

强跳过在 Kotlin 2.0.20 中默认启用。

要在 2.0.20 之前的版本中为 Gradle 模块启用强跳过,请在 Gradle 配置的 composeCompiler 代码块中包含以下选项:

android { ... }

composeCompiler {
   enableStrongSkippingMode = true
}

可组合项的可跳过性

强跳过模式放宽了 Compose 编译器在跳过和可组合函数方面通常应用的一些稳定性规则。默认情况下,如果可组合函数的所有参数都具有稳定的值,Compose 编译器会将其标记为可跳过。强跳过模式改变了这一点。

启用强跳过后,所有可重启的可组合函数都变为可跳过。这适用于它们是否具有不稳定参数。不可重启的可组合函数仍然不可跳过。

何时跳过

为了确定在重组期间是否跳过可组合项,Compose 会将每个参数的值与其之前的值进行比较。比较类型取决于参数的稳定性

  • 不稳定参数使用实例相等性 (===) 进行比较
  • 稳定参数使用对象相等性 (Object.equals()) 进行比较

如果所有参数都满足这些要求,Compose 将在重组期间跳过该可组合项。

您可能希望某个可组合项选择退出强跳过。也就是说,您可能希望一个可重启但不可跳过的可组合项。在这种情况下,请使用 @NonSkippableComposable 注解。

@NonSkippableComposable
@Composable
fun MyNonSkippableComposable {}

将类注解为稳定

如果您希望某个对象使用对象相等性而不是实例相等性,请继续使用 @Stable 注解该类。您可能需要这样做的例子是观察整个对象列表时,例如 Room 等数据源会在列表中的任何项发生更改时为列表中的每个项分配新对象。

Lambda 记忆化

强跳过模式还允许对可组合项内部的 Lambda 进行更多记忆化。启用强跳过后,可组合函数内的每个 Lambda 都将自动被记住。

示例

为了在使用强跳过时实现可组合项内部 Lambda 的记忆化,编译器会使用 remember 调用来封装您的 Lambda。它以 Lambda 的捕获作为键。

考虑以下示例中的 Lambda 情况:

@Composable
fun MyComposable(unstableObject: Unstable, stableObject: Stable) {
    val lambda = {
        use(unstableObject)
        use(stableObject)
    }
}

启用强跳过后,编译器通过将其封装在 remember 调用中来记忆化 Lambda:

@Composable
fun MyComposable(unstableObject: Unstable, stableObject: Stable) {
    val lambda = remember(unstableObject, stableObject) {
        {
            use(unstableObject)
            use(stableObject)
        }
    }
}

键遵循与可组合函数相同的比较规则。运行时使用实例相等性比较不稳定键。它使用对象相等性比较稳定键。

记忆化与重组

这种优化大大增加了运行时在重组期间跳过的可组合项的数量。如果没有记忆化,运行时在重组期间更有可能为任何接受 Lambda 参数的可组合项分配新的 Lambda。结果,新的 Lambda 的参数与上次组合的参数不相等。这导致了重组。

避免记忆化

如果您有不想记忆化的 Lambda,请使用 @DontMemoize 注解。

val lambda = @DontMemoize {
    ...
}

APK 大小

编译时,可跳过的可组合项会比不可跳过的可组合项生成更多的代码。启用强跳过后,编译器会将几乎所有可组合项标记为可跳过,并将所有 Lambda 封装在 remember{...} 中。因此,启用强跳过模式对您的应用的 APK 大小影响非常小。

Now In Android中启用强跳过,APK 大小增加了 4KB。大小差异主要取决于给定应用中先前不可跳过的可组合项的数量,但应该相对较小。