Strong Skipping – это режим, доступный в компиляторе Compose. Если он включен, поведение компилятора меняется двумя способами:
- Composable-функции с нестабильными параметрами становятся с возможностью пропуска.
- Лямбда-выражения с нестабильными захватами запоминаются.
Включить режим строгого пропуска
В Kotlin 2.0.20 строгий пропуск включен по умолчанию.
Чтобы включить режим строгого пропуска для модуля Gradle в выпуске до 2.0.20, добавьте в блок composeCompiler конфигурации Gradle следующий параметр:
android { ... }
composeCompiler {
enableStrongSkippingMode = true
}
Возможность пропуска для составных объявлений
Режим строгого пропуска смягчает некоторые правила стабильности, которые обычно применяются компилятором Compose, когда речь идет о пропуске и composable-функциях. По умолчанию компилятор Compose помечает composable-функцию как пропускаемую, если все ее аргументы имеют стабильные значения. Режим строгого пропуска меняет это поведение.
Если включить строгий пропуск, все перезапускаемые composable-функции станут пропускаемыми. Это относится и к тем, у кого нет нестабильных параметров. Неперезапускаемые composable-функции остаются непропускаемыми.
Когда пропускать
Чтобы определить, следует ли пропустить composable-функцию во время рекомпозиции, Compose сравнивает значение каждого параметра с его предыдущим значением. Тип сравнения зависит от стабильности параметра.
- Нестабильные параметры сравниваются с помощью равенства экземпляров (
===). - Стабильные параметры сравниваются с помощью равенства объектов (
Object.equals()).
Если все параметры соответствуют этим требованиям, Compose пропускает composable-функцию во время рекомпозиции.
Вы можете отключить строгий пропуск для composable-функции. То есть вам может понадобиться перезапускаемая composable-функция без возможности пропуска. В этом случае используйте аннотацию @NonSkippableComposable.
@NonSkippableComposable
@Composable
fun MyNonSkippableComposable {}
Как помечать классы как стабильные
Если вы хотите, чтобы объект сравнивался с использованием равенства объектов (Object.equals()), а не равенства экземпляров (===), продолжайте аннотировать данный класс с помощью @Stable или @Immutable.
Прежде чем добавлять @Stable или @Immutable, учтите следующее:
- Классы со стабильными свойствами в модулях Compose уже стабильны. Класс со свойствами
valстабильных типов в модуле, скомпилированном с помощью компилятора Compose, автоматически определяется как стабильный и использует.equals()без необходимости в@Immutableили@Stable. - Добавляйте аннотации только к классам с быстрыми проверками
.equals(). Добавляйте@Stableили@Immutable, когда компилятор не может определить стабильность (например, для классов из модулей, не относящихся к Compose) и часто выделяются новые экземпляры с идентичными значениями, при условии, что проверка.equals()класса выполняется быстро. - Будьте осторожны, добавляя аннотации к моделям с коллекциями. Если вы добавите
@Stableили@Immutableк моделям, содержащим коллекции (или заменитеListнаImmutableList), Compose будет сравнивать каждый элемент коллекции с.equals()при каждом проходе рекомпозиции. Для больших коллекций или моделей, используемых в ленивых макетах (LazyColumn,LazyRow,LazyGrid), сравнение каждого элемента может быть медленнее, чем использование равенства экземпляров (===) или прямая рекомпозиция.
Мемоизация лямбда-функций
Режим строгого пропуска также позволяет использовать больше мемоизации лямбда-выражений внутри composable-функций. Если включить строгий пропуск, каждая лямбда-функция внутри composable-функции будет автоматически запоминаться.
Примеры
Чтобы добиться мемоизации лямбда-выражений внутри composable-функций при использовании строгого пропуска, компилятор оборачивает лямбда-выражение вызовом remember. Она связана с захватами лямбда-выражения.
Рассмотрим случай, когда у вас есть лямбда-выражение, как в следующем примере:
@Composable
fun MyComposable(unstableObject: Unstable, stableObject: Stable) {
val lambda = {
use(unstableObject)
use(stableObject)
}
}
Если включен режим строгого пропуска, компилятор запоминает лямбда-выражение, упаковывая его в вызов remember:
@Composable
fun MyComposable(unstableObject: Unstable, stableObject: Stable) {
val lambda = remember(unstableObject, stableObject) {
{
use(unstableObject)
use(stableObject)
}
}
}
Ключи следуют тем же правилам сравнения, что и составные функции. Среда выполнения сравнивает нестабильные ключи, используя равенство экземпляров. Он сравнивает стабильные ключи, используя равенство объектов.
Мемоизация и рекомпозиция
Эта оптимизация значительно увеличивает количество composable-функций, которые среда выполнения пропускает во время рекомпозиции. Без мемоизации среда выполнения с гораздо большей вероятностью выделит новую лямбду для любой composable-функции, которая принимает лямбда-параметр во время рекомпозиции. В результате у новой лямбда-функции будут параметры, не равные последней композиции. Это приводит к рекомпозиции.
Избегайте мемоизации
Если у вас есть лямбда-выражение, которое вы не хотите запоминать, используйте аннотацию @DontMemoize.
val lambda = @DontMemoize {
...
}
Размер APK-файла
При компиляции пропускаемых composable-функций генерируется больше кода, чем при компиляции непропускаемых. Если включить строгий пропуск, компилятор пометит почти все composable-функции как пропускаемые и обернет все лямбда-выражения в remember{...}. Поэтому включение режима сильного пропуска очень мало влияет на размер APK-файла вашего приложения.
Включение строгого пропуска в Now In Android увеличило размер APK на 4 кБ. Разница в размере в основном зависит от количества ранее непропускаемых composable-функций, которые присутствовали в данном приложении, но должна быть относительно незначительной.