强烈的跳过模式

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

  • 包含不稳定的参数的可组合项将变为可跳过
  • 捕获不稳定的 lambda 会记住

启用强跳过模式

如需为 Gradle 模块启用强跳过功能,请在 Gradle 配置的 composeCompiler 代码块中添加以下选项:

composeCompiler {
   enableStrongSkipping = 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 调用中来记忆 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。大小差异在很大程度上取决于给定应用中存在之前不可跳过的可组合项的数量,但应该相对较小。