Если у вас возникли проблемы с производительностью, вызванные ненужной или чрезмерной рекомпозицией, вам следует отладить стабильность вашего приложения. В этом руководстве описано несколько методов, как это сделать.
Инспектор макетов
Инспектор макетов в Android Studio позволяет увидеть, какие composable-функции рекомпонуются в приложении. Он показывает, сколько раз Compose рекомпоновал или пропускал компонент.

Отчеты компилятора Compose
Компилятор Compose может выводить результаты вывода стабильности для проверки. Используя эти выходные данные, вы можете определить, какие из ваших компонуемых объектов можно пропустить, а какие нет. В следующих подразделах кратко описано, как использовать эти отчеты. Более подробную информацию можно найти в технической документации.
Настроить
Отчеты компилятора Compose по умолчанию отключены. Вы можете активировать их с помощью флага компилятора. Точная настройка зависит от проекта, но для проектов, использующих плагин gradle компилятора Compose, вы можете добавить в файл build.gradle каждого модуля следующее:
android { ... }
composeCompiler {
reportsDestination = layout.buildDirectory.dir("compose_compiler")
metricsDestination = layout.buildDirectory.dir("compose_compiler")
}
Теперь при сборке проекта будут создаваться отчеты компилятора Compose.
Пример выходных данных
reportsDestination создает три файла. Ниже приведены примеры выходных данных из JetSnack.
<modulename>-classes.txt. Отчет о стабильности классов в этом модуле. Пример.<modulename>-composables.txt. Отчет о том, какие composable-функции в модуле можно перезапускать и пропускать. Пример.<modulename>-composables.csv: версия отчета о composable-функциях в форматеCSV, которую можно импортировать в таблицу или обработать с помощью скрипта. Пример
Отчет о composable-функциях
В файле composables.txt содержится подробная информация о каждой composable-функции для заданного модуля, включая стабильность их параметров, а также возможность перезапуска или пропуска. Ниже приведен гипотетический пример из JetSnack.
restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun SnackCollection(
stable snackCollection: SnackCollection
stable onSnackClick: Function1<Long, Unit>
stable modifier: Modifier? = @static Companion
stable index: Int = @static 0
stable highlight: Boolean = @static true
)
В этом примере каждый параметр SnackCollection помечен как stable, поэтому Compose сравнивает каждый параметр, используя структурное равенство (Object.equals()). Хотя параметры stable идеально подходят для примитивных значений и небольших плоских классов данных с дешевыми проверками .equals(), принудительное использование моделей, содержащих коллекции (например, SnackCollection, который оборачивает List<Snack>), в качестве stable заставляет Compose вызывать .equals() для каждого элемента при каждой рекомпозиции. Если включен режим строгого пропуска, рекомендуется сохранять модели, содержащие коллекции unstable, чтобы Compose сравнивал их, используя равенство экземпляров (===).
Рассмотрим другой пример, когда режим строгого пропуска отключен:
restartable scheme("[androidx.compose.ui.UiComposable]") fun HighlightedSnacks(
stable index: Int
unstable snacks: List<Snack>
stable onSnackClick: Function1<Long, Unit>
stable modifier: Modifier? = @static Companion
)
Если режим строгого пропуска отключен (до Kotlin 2.0.20), composable-функция HighlightedSnacks не пропускается. Compose никогда не пропускает ее во время рекомпозиции, даже если ни один из ее параметров не изменился, из-за параметра unstable snacks.
Если включен режим строгого пропуска (по умолчанию в Kotlin 2.0.20 и более поздних версиях), HighlightedSnacks помечается как skippable, а Compose сравнивает параметр unstable snacks, используя равенство экземпляров (===). В результате Compose пропускает HighlightedSnacks, если передается та же ссылка List, и перекомпонует ее, только если передается вновь выделенный экземпляр List.
Отчет о курсах
Файл classes.txt содержит аналогичный отчет о классах в указанном модуле. Ниже приведен фрагмент кода, который возвращается для класса Snack:
unstable class Snack {
stable val id: Long
stable val name: String
stable val imageUrl: String
stable val price: Long
stable val tagline: String
unstable val tags: Set<String>
<runtime stability> = Unstable
}
Ниже приведено определение термина "Snack".
data class Snack(
val id: Long,
val name: String,
val imageUrl: String,
val price: Long,
val tagline: String = "",
val tags: Set<String> = emptySet()
)
Компилятор Compose пометил Snack как нестабильный. Это связано с тем, что параметр tags имеет тип Set<String>. Это неизменяемый тип, поскольку он не является MutableSet. Однако стандартные классы коллекций, такие как Set, List и Map, в конечном счете являются интерфейсами. Таким образом, базовая реализация все равно может быть изменяемой.
Например, вы можете написать val set: Set<String> = mutableSetOf("foo"). Переменная является константой, а ее объявленный тип неизменяем, но ее реализация все ещё изменяема. Компилятор Compose не может быть уверен в неизменяемости этого класса, поскольку он видит только объявленный тип. Поэтому он помечает tags как нестабильный.