Стабильность в Compose

Compose считает типы стабильными или нестабильными. Тип считается стабильным, если он неизменяемый или если Compose может определить, изменилось ли его значение между повторными композициями. Тип считается нестабильным, если Compose не может определить, изменилось ли его значение между перекомпозициями.

Compose использует стабильность параметров composable-функции, чтобы определить, как сравнивать входные данные и можно ли пропустить composable-функцию во время рекомпозиции (с сильным режимом пропуска, который по умолчанию включен в Kotlin 2.0.20 и более поздних версиях):

  • Стабильные параметры. Compose сравнивает стабильные параметры с помощью структурного равенства (Object.equals()). Если стабильные параметры компонента равны предыдущим значениям, Compose пропускает его.
  • Нестабильные параметры. Если включен строгий режим пропуска (по умолчанию в Kotlin 2.0.20 и более поздних версиях), Compose сравнивает нестабильные параметры с помощью равенства экземпляров (===) и пропускает composable-функцию, если передаются одни и те же экземпляры объектов. В версиях до Kotlin 2.0.20 (или когда режим пропуска с проверкой отключен) Compose всегда повторно составляет компонуемый элемент с нестабильными параметрами, когда его родительский элемент составляется повторно.

Если ваше приложение часто выделяет новые экземпляры нестабильных параметров или выполняет дорогостоящие проверки больших коллекций, чрезмерно аннотируя модели, вы можете заметить ненужные перекомпозиции или избыточные сравнения..equals()

В этой статье рассказывается, как Compose определяет стабильность и как ее можно оптимизировать, чтобы повысить производительность и улучшить пользовательский опыт.

Неизменяемые объекты

В приведенных ниже фрагментах кода показаны общие принципы стабильности и перекомпоновки.

Класс Contact – это неизменяемый класс данных. Это связано с тем, что все его параметры являются примитивами, определенными с помощью ключевого слова val. После создания экземпляра Contact изменить значение свойств объекта нельзя. Если вы попытаетесь это сделать, будет создан новый объект.

data class Contact(val name: String, val number: String)

У composable-функции ContactRow есть параметр типа Contact.

@Composable
fun ContactRow(contact: Contact, modifier: Modifier = Modifier) {
   var selected by remember { mutableStateOf(false) }

   Row(modifier) {
      ContactDetails(contact)
      ToggleButton(selected, onToggled = { selected = !selected })
   }
}

Подумайте, что произойдет, когда пользователь нажмет на переключатель и состояние selected изменится:

  1. Compose определяет, нужно ли перекомпоновать код внутри ContactRow.
  2. Она видит, что единственный аргумент для ContactDetails имеет тип Contact.
  3. Поскольку Contact – это неизменяемый класс данных, Compose уверен, что ни один из аргументов для ContactDetails не изменился.
  4. Поэтому функция "Создать" пропускает ContactDetails и не перефразирует его.
  5. С другой стороны, аргументы для ToggleButton изменились, и Compose перекомпоновывает этот компонент.

Изменяемые объекты

В приведенном выше примере используется неизменяемый объект, но можно создать и изменяемый. Рассмотрим следующий фрагмент кода:

data class Contact(var name: String, var number: String)

Поскольку каждый параметр Contact теперь является var, класс больше не является неизменяемым. Если свойства изменились, Compose не узнает об этом. Это связано с тем, что Compose отслеживает только изменения объектов состояния.

Compose считает такой класс нестабильным. В строгом режиме пропуска (Kotlin 2.0.20+) Compose сравнивает Contact с помощью равенства экземпляров (===): изменение contact.name на месте не приведет к повторной композиции, а передача нового экземпляра Contact с идентичными значениями все равно заставит ContactDetails выполнить повторную композицию (без строгого режима пропуска ContactDetails выполняет повторную композицию каждый раз, когда меняется selected).

Реализация в Compose

Полезно, хотя и не обязательно, понимать, как именно Compose определяет, какие функции следует пропустить при повторной композиции.

Когда компилятор Compose обрабатывает ваш код, он отмечает каждую функцию и тип одним из нескольких тегов. Эти теги отражают то, как Compose обрабатывает функцию или тип во время повторной композиции.

Функции

Compose может отмечать функции как skippable или restartable. Обратите внимание, что функция может быть отмечена как одна из этих категорий, обе или ни одна из них:

  • Возможность пропуска. Если компилятор отмечает компонуемый элемент как пропускаемый, Compose может пропустить его во время повторной композиции, если все его аргументы равны предыдущим значениям. (Если в Kotlin 2.0.20+ включен строгий режим пропуска, все композиции, которые можно перезапустить, автоматически становятся пропускаемыми.)
  • С возможностью перезапуска. Компонуемый элемент с возможностью перезапуска служит областью, в которой может начаться повторная композиция. Другими словами, функция может быть точкой входа, с которой Compose начнет повторно выполнять код для перекомпоновки после изменения состояния.

Типы

Типы аннотаций Compose могут быть неизменяемыми или стабильными. Каждый тип относится к одной из двух категорий:

  • Неизменяемый. Compose отмечает тип как неизменяемый, если значение его свойств никогда не может измениться и все методы являются референциально прозрачными.
    • Обратите внимание, что все примитивные типы помечены как неизменяемые. К ним относятся String, Int и Float.
  • Стабильный – тип, свойства которого можно изменить после создания. Если во время выполнения приложения эти свойства меняются, Compose узнает об этом.

Отладка стабильности

Если ваше приложение повторно составляет composable-функцию, параметры которой не изменились, сначала проверьте, выделяются ли новые экземпляры нестабильных типов при каждой рекомпозиции (или, если режим пропуска Strong отключен, есть ли у composable-функции параметры со свойствами var или val нестабильного типа).

Подробную информацию о том, как диагностировать сложные проблемы со стабильностью в Compose, можно найти в руководстве по отладке стабильности.

Как устранить проблемы со стабильностью

Чтобы узнать, как обеспечить стабильность реализации Compose, ознакомьтесь с руководством по устранению проблем со стабильностью.

Сводка

В целом следует учитывать следующие моменты:

  • Параметры. Compose определяет стабильность каждого параметра ваших композиций, чтобы решить, сравнивать ли их с помощью структурного равенства (.equals()) или равенства экземпляров (===) во время повторной композиции.
  • Немедленные исправления. Если вы заметили, что ваша composable-функция не пропускается и это приводит к проблемам производительности, проверьте, не создаются ли новые экземпляры нестабильных параметров при каждом проходе или не используются ли свойства var вместо State.
  • Отчеты компилятора. С помощью отчетов компилятора можно определить, какая стабильность подразумевается для ваших классов.
  • Коллекции. Compose считает стандартные интерфейсы коллекций (List, Set и Map) нестабильными, поскольку их базовые реализации могут быть изменяемыми. В строгом режиме пропуска (включен по умолчанию в Kotlin 2.0.20 и более поздних версий) нестабильные коллекции по-прежнему пропускают повторную композицию, используя быстрое сравнение экземпляров O(1) на равенство (===), если ссылка на коллекцию не меняется. Не преобразуйте большие коллекции в неизменяемые коллекции Kotlinx и не добавляйте аннотации @Immutable или @Stable к классам, содержащим коллекции, если структурное равенство (O(N) .equals()) не требуется, поскольку сравнение каждого элемента при каждой повторной композиции может быть дороже, чем сама повторная композиция.
  • Другие модули. Compose считает классы из модулей, в которых не выполняется компилятор Compose, нестабильными (сравнение выполняется с помощью === в режиме Strong skipping). Если источник данных часто повторно создает небольшие плоские модели из модулей, не относящихся к Compose, с идентичными значениями, вы можете настроить файл конфигурации стабильности или использовать @Stable или @Immutable, где .equals() – недорогой.

Дополнительные ресурсы