Найти в Дзене

Заметки про Compose

Оглавление

Относительно недавно я начала использовать Compose. Буду собирать небольшие заметки по мере изучения.

Первое, что мы видим открывая любой проект на Compose — аннотация @Composable. Разберём, что это такое.

@Composable — это специальная аннотация, которая позволяет описывать компоненты UI (пользовательский интерфейс) как функции вместо объектов и автоматически отслеживать изменение состояния. Т.е. раньше у нас были xml, а теперь функции. И все функции, которые должны как-то рисоваться на экране, должны быть с аннотацией.

А ещё, такая функция может быть вызвана только из другой @Composable функции. Также они ничего не возвращают обычно. Там просто описывается UI.

Пример использования:

@Composable
fun Greeting(name: String) {
Text("Hello $name!")
}

Тут мы не сможем вызвать функцию Text("Hello $name!") внутри обычного Kotlin метода, потому что у Text аннотация @Composable.

Вот что происходит под капотом:

  1. Во время компиляции компилятор вместе с Jetpack Compose превращает каждую @Composable функцию в обычную Kotlin функцию, которая принимает дополнительный параметр — "Composer". Composer — это объект, который управляет отслеживанием состояния и перерисовкой.
  2. Когда мы вызываем @Composable функцию, Composer записывает этот вызов в специальную структуру данных, называемую "slot table" (я не знаю как это перевести, но вот оно: https://android.googlesource.com/platform/frameworks/support/+/refs/heads/androidx-main/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SlotTable.kt). В slot table сохраняется весь порядок вызовов и текущее состояние каждого Composable элемента.
  3. Когда состояние меняется, Composer перебирает slot table и вызывает функции, связанные с измененным состоянием. Это позволяет автоматически обновлять интерфейс при изменении состояния. Звучит очень здорово, потому что не нужно вручную всё делать. Достаточно просто описать состояния.
  4. Composer использует эффективные алгоритмы для минимального количества перерисовок (так все говорят). Будут перерисованы только те элементы, которые изменились.

Немного рекомендаций:

  1. Состояние в Compose — это то, что может измениться в любой момент и вызвать перерисовку. Лучше всего вынести состояние на самый верхний уровень, чтобы делить его между несколькими функциями, чтобы не было лишних перерисовок и можно было легко отслеживать и дебажить.
  2. Используйте LaunchedEffect и другие специальные функции для работы с корутинами, таймерами, подписками и т.п. Эти функции учитывают время жизни Composable функции и отменят корутину, если функция будет уничтожена. Очень сильно поможет с утечками памяти.
  3. Делайте функции @Composable маленькими. С этой проблемой я столкнулась на практике, что функция становится слишком большой и её сложно читать и там появляется ненужная логика.
  4. Передавайте состояние с верхнего уровня внутрь @Composable функций, а не меняйте его внутри. Это поможет с тем, чтобы не было лишних перерисовок и чтобы состояние не сбрасывалось (например, при повороте экрана).
  5. Не вызывайте @Composable функции внутри циклов, условных операторов или лямбда-выражений. Я долго думала про этот совет и почему не стоит вызывать внутри if-else, но всё сводится к чистоте кода и чтобы не перерисовывалось лишний раз.
  6. Так как @Composable функции могут вызываться в любой момент для перерисовки интерфейса, важно, чтобы они работали быстро. Не забываем про корутины и т.п., что поможет нам избежать блокировки основного потока.
  7. Используйте remember для сохранения состояния, чтобы избежать повторных перерисовок. Тут, кстати, я знаю, что многие против remember, но я не углублялась пока что в эту тему. На данный момент я его использую.
  8. Можно использовать @NonRestartableComposable для функций, которые не должны перезапускаться при изменении переданных параметров. Я не использовала на практике, но наверняка есть ситуации, когда пригодится.
  9. Используйте LazyColumn и LazyRow для больших списков. Это что-то вроде аналога RecyclerView и элементы будут создаваться и уничтожаться по мере прокрутки, вместо создания всех элементов сразу.

Дубль статей в телеграмме — https://t.me/android_junior

Мои заметки в телеграмме — https://t.me/android_junior_notes

P.S. сделано с помощью ChatGPT. :)

Ещё помогла статья: https://lhwdev.github.io/note/compose/how-it-works/