强烈的跳过模式

强式跳过是 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)
    }
}

启用强力跳过后,编译器会通过将 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,请使用 @DontMemoize 注解。

val lambda = @DontMemoize {
    ...
}

APK 大小

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

Now In Android 中启用强跳过使 APK 大小增加了 4kB。大小差异在很大程度上取决于给定应用中之前不可跳过的可组合项的数量,但应该相对较小。