强跳过模式

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

  • 具有不稳定参数的可组合函数(Composable)将变为可跳过的。
  • 具有不稳定捕获的 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。大小差异在很大程度上取决于给定应用程序中先前不可跳过的可组合函数的数量,但应该相对较小。