Strong Skipping is a mode available in the Compose compiler. When enabled, it changes the compiler's behavior in two ways:
- Composables with unstable parameters become skippable
- Lambdas with unstable captures are remembered
Enable strong skipping mode
To enable strong skipping for a Gradle module in a previous release,
include the following option in
the composeCompiler
block of your Gradle configuration:
android { ... }
composeCompiler {
enableStrongSkippingMode = true
}
Composable skippability
Strong skipping mode relaxes some of the stability rules normally applied by the Compose compiler when it comes to skipping and composable functions. By default, the Compose compiler marks a composable function as skippable if all of its arguments have stable values. Strong skipping mode changes this.
With strong skipping enabled, all restartable composable functions become skippable. This applies whether or not they have unstable parameters. Non-restartable composable functions remain unskippable.
When to skip
To determine whether to skip a composable during recomposition, Compose compares the value of each parameter with their previous values. The type of comparison depends on the stability of the parameter.
- Unstable parameters are compared using instance equality (
===
) - Stable parameters are compared using object equality (
Object.equals()
)
If all parameters meet these requirements, Compose skips the composable during recomposition.
You might want a composable to opt out of strong skipping. That is, you might
want a restartable but non-skippable composable. In this case, use the
@NonSkippableComposable
annotation.
@NonSkippableComposable
@Composable
fun MyNonSkippableComposable {}
Annotate classes as stable
If you want an object using object equality instead of instance equality,
continue to annotate the given class with @Stable
.
An example of when you might
have to do this is when observing an entire list of objects, data sources such
as Room will allocate new objects for every item in the list any time one of
them changes.
Lambda memoization
Strong skipping mode also enables more memoization of lambdas inside composables. With strong skipping enabled, every lambda inside a composable function will be automatically remembered.
Examples
To achieve memoization of lambdas inside composables when using strong skipping,
the compiler wraps your lambda with a remember
call. It is keyed with the
captures of the lambda.
Consider a case where you have a lambda as in the following example:
@Composable
fun MyComposable(unstableObject: Unstable, stableObject: Stable) {
val lambda = {
use(unstableObject)
use(stableObject)
}
}
With strong skipping enabled, the compiler memoizes the lambda by wrapping it in
a remember
call:
@Composable
fun MyComposable(unstableObject: Unstable, stableObject: Stable) {
val lambda = remember(unstableObject, stableObject) {
{
use(unstableObject)
use(stableObject)
}
}
}
The keys follow the same comparison rules as composable functions. The runtime compares unstable keys using instance equality. It compares stable keys using object equality.
Memoization and recomposition
This optimization greatly increases the number of composables that the runtime skips during recomposition. Without memoization, the runtime is much more likely to allocate a new lambda to any composable that takes a lambda parameter during recomposition. As a result, the new lambda has parameters that are not equal to the last composition. This results in recomposition.
Avoid memoization
If you have a lambda that you don't want to memoize, use the @DontMemoize
annotation.
val lambda = @DontMemoize {
...
}
APK Size
When compiled, skippable Composables result in more generated code than
composables that are not skippable. With strong skipping enabled, the compiler
marks nearly all composables as skippable and wraps all lambdas in a
remember{...}
. Due to this, enabling strong skipping mode has a very small
impact on the APK size of your application.
Enabling strong skipping in Now In Android increased the APK size by 4kB. The difference in size largely depends on the number of previously unskippable composables that were present in the given app but should be relatively minor.