强烈的跳过模式

强式跳过是 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 的 memoization,编译器会使用 remember 调用封装您的 lambda。其键是 lambda 的捕获。

假设您有一个 lambda,如以下示例所示:

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

启用强力跳过后,编译器会通过将 lambda 封装在 remember 调用中来对其进行 memoization:

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

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

记忆化和重组

这项优化大大增加了运行时在重组期间跳过的可组合项的数量。如果不进行 memoization,运行时更有可能在重组期间向接受 lambda 参数的任何可组合项分配新的 lambda。因此,新 lambda 的参数与上一个组合不等。这会导致重新组合。

避免 memoization

如果您不想对某个 lambda 进行 memoization,请使用 @DontMemoize 注解。

val lambda = @DontMemoize {
    ...
}

APK 大小

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

现在用 Android 中启用强制跳过功能会使 APK 大小增加 4kB。大小的差异很大程度上取决于给定应用中存在的以前不可跳过的可组合项的数量,但这些可组合项应该相对较小。