强跳过模式

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

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

启用强跳过模式

要在先前版本中为 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。大小差异在很大程度上取决于给定应用中先前不可跳过的可组合项的数量,但应该相对较小。