Введение: база, без которой Compose не понять
«Расскажите, как работает Compose» — с этой фразы начинается половина современных Android-собеседований. И большинство кандидатов отвечает общими словами: декларативный UI, функции вместо XML, что-то про перерисовку. Формально — правда, но глубины нет.
Compose — это не «ещё один способ верстать», а другая модель мышления: вы не описываете, как менять экран, а описываете, каким экран должен быть при текущих данных, и система сама приводит UI к этому виду. Если держите в голове эту мысль — половина вопросов про Compose отпадает сама собой.
На собеседованиях я обычно проверяю три вещи: composable-функции, рекомпозицию с состоянием и Modifier. Разберём каждую — с кодом и типичными промахами кандидатов.
Composable-функции: UI как обычный код
Базовая единица Compose — composable-функция: обычная функция Kotlin, помеченная аннотацией @Composable. Она принимает данные и описывает, как выглядит UI. Минимальный пример — из официальной документации:
@Composable
fun Greeting(name: String) {
Text(text = "Привет, $name!")
}Три правила, которые любят спрашивать:
- Функция не возвращает значение. Она описывает UI, а не создаёт объекты-виджеты. В отличие от View-системы, здесь нет дерева объектов, которое вы храните и обновляете вручную.
- Функция идемпотентна. Один и тот же аргумент — один и тот же результат. Поэтому в composable не место вызовам
random()и чтению глобальных переменных. - Функция быстрая и без побочных эффектов. Composable может выполняться каждый кадр — например, при анимации. Кладёте туда тяжёлую работу или запись в базу — получаете лаги и неожиданное поведение.
Как запомнить
Composable-функция — это чистая функция, которая превращает данные в описание UI. Не возвращает объекты, не имеет побочных эффектов, должна быть быстрой — потому что Compose может вызывать её многократно.
Типичный ответ, который я слышу на собеседовании: «Compose — это когда UI пишется функциями, а не XML». Это правда, но не суть. Суть — декларативность: вы не обновляете виджеты вручную, а описываете UI от состояния. Если кандидат доходит до этого сам — ставлю плюс.
Рекомпозиция и состояние: remember и mutableStateOf
Раз UI описан функциями, обновление экрана — это повторный вызов этих функций с новыми данными. Процесс называется рекомпозицией: Compose перечитывает входные данные и пересоздаёт только те части UI, чьи данные изменились. Всё, что не изменилось, перекомпоновывается со скипом — это официальное поведение, а не оптимизация «как получится».
Классический пример — счётчик кликов:
@Composable
fun Counter() {
var count by remember { mutableStateOf(0) }
Column(modifier = Modifier.padding(16.dp)) {
Text(text = "Кликов: $count")
Button(onClick = { count++ }) {
Text("+1")
}
}
}remember { mutableStateOf(0) } создаёт наблюдаемое состояние, интегрированное со средой выполнения Compose. Запись в count планирует рекомпозицию — и все composable-функции, читающие count, пересоздаются автоматически. TextField устроен так же: он не обновляет себя сам, а показывает значение из параметра value и сообщает об изменениях через onValueChange — поэтому без remember ввод текста «не работает».
Важно понимать, что рекомпозиция — не перерисовка «всего экрана». Compose умно выбирает, какие функции перезапустить: перекомпоновывается только то, чьи входные данные изменились, остальное пропускается. Именно поэтому composable-функции должны быть идемпотентными — если функция при одном и том же аргументе даёт разный результат, Compose может «пропустить» перекомпоновку, и пользователь увидит устаревший UI.
Тонкости, которые спрашивают дальше:
rememberхранит значение в композиции и забывает его, когда composable покидает дерево.rememberне переживает смену конфигурации — поворот экрана сбросит значение. Для этого естьrememberSaveable, который автоматически сохраняет всё, что умеет класть вBundle.- Состояние — единственный источник правды. Нельзя держать данные и в state, и в глобальной переменной — рассинхронизируетесь.
Официальное руководство по состоянию — State и Jetpack Compose. Пожалуй, самый важный документ для подготовки: в нём же разбирается поднятие состояния (state hoisting) — паттерн, при котором composable становится безгосударственным, а состояние и колбэки передаются извне. Паттерн value + onValueChange из него перекочевал и на собеседования — «как сделать компонент переиспользуемым?» отвечается именно через него.
Ошибка-классика: кандидат уверенно рассказывает про рекомпозицию, но не может объяснить, почему простое var count = 0 не обновит экран. Ответ: Compose отслеживает только наблюдаемое состояние (MutableState через mutableStateOf), обычная переменная рекомпозицию не запускает.
Modifier: цепочки и порядок
Второй столп Compose — Modifier: упорядоченная, неизменяемая цепочка элементов, которая описывает размер, отступы, клики, фон и другие аспекты внешнего вида и поведения.
@Composable
fun ProfileCard(modifier: Modifier = Modifier) {
Row(
modifier = modifier
.fillMaxWidth()
.padding(16.dp)
.clickable { /* открыть профиль */ }
) {
Text(text = "Имя пользователя")
}
}Две вещи, которые важно понять. Первая: composable-функция принимает modifier параметром и передаёт его первому элементу, генерирующему UI — это официальная рекомендация, так компоненты остаются переиспользуемыми. Вторая: порядок модификаторов имеет значение. padding после clickable — кликабельной становится область вместе с отступом; поменяете порядок — отступ кликабельности не получит. Это прямо описано в документации по модификаторам.
Часто используемые: size, fillMaxWidth, fillMaxSize, padding, offset, weight (внутри Row/Column), background, clip, border, clickable.
Правило порядка
Цепочка модификаторов читается сверху вниз, и каждый следующий применяется к результату предыдущего. Запомнили — половина «странных багов» с кликами и отступами отпадает.
Компоновки и MaterialTheme
UI собирается из трёх базовых контейнеров: Column (вертикально), Row (горизонтально), Box (наложение). Списки — LazyColumn и LazyRow, аналог RecyclerView с ленивой загрузкой.
Тема оформляется через MaterialTheme, который снабжает дочерние компоненты цветовой схемой, типографикой и формами. Современный стандарт — Material 3 (зависимость androidx.compose.material3:material3):
private val LightColors = lightColorScheme(
primary = Color(0xFF00696D),
secondary = Color(0xFF4A6365)
)
@Composable
fun AppTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
content: @Composable () -> Unit
) {
MaterialTheme(
colorScheme = if (darkTheme) darkColorScheme() else LightColors,
content = content
)
}isSystemInDarkTheme() подстраивается под системную тему, а на Android 12+ можно использовать динамические цвета, сгенерированные из обоев пользователя. Детали — в руководстве по Material Design 3.
Типичные ошибки на собеседовании
- Побочные эффекты в composable. Сетевой запрос или запись в БД прямо в теле функции — «стрельба в ногу»: функция может выполниться несколько раз. Для эффектов есть
LaunchedEffectи другие API. - Состояние не через remember. Обычная переменная не триггерит рекомпозицию — экран не обновится.
- Порядок модификаторов.
clickableиpaddingв разных порядках дают разное поведение. - Тяжёлая работа в composable. Выполняется вплоть до каждого кадра — анимации «фризят».
- Ответ только теорией. «Compose — декларативный UI» без примера кода — ответ уровня учебника, а не практика.
Кита Compose
Composable-функции, рекомпозиция и Modifier — это 80% вопросов про Compose на собеседованиях. Состояние и темы — следующий уровень, но без трёх китов он не читается.
Мой совет по подготовке: не заучивайте определения — напишите руками экран со списком, счётчиком и формой. Пока вы не увидели, как состояние «прокидывается» через параметры и колбэки, Compose не «щёлкнет».
И ещё одно практическое наблюдение. На собеседованиях я часто прошу написать маленький экран — «список с кнопкой удаления» или «форму с валидацией». И вот тут всплывает всё: где живёт состояние, что делать с rememberSaveable, куда девать modifier. Тренируйтесь именно так — руками, на время, как на интервью. Полчаса в день таких упражнений дают больше, чем неделя чтения документации.
Пара слов обо мне: я Рустем Бикбулатов, senior Android-разработчик, провожу технические собеседования и менторю разработчиков, в том числе как ментор Яндекс Практикума. За плечами более 100 учеников и разборов — и Compose одна из тем, где кандидаты с опытом 1–2 года чаще всего плавают именно на состоянии и рекомпозиции. Есть диагностика: разбор вашего уровня и план закрытия пробелов.
Хотите понять, какие пробелы мешают вам расти?
Бесплатная диагностика: разберу ваши знания Compose, Kotlin и архитектуры и составлю план роста под ваш уровень.
Итоги
Compose — декларативный UI-тулкит, и база для работы и собеседований помещается в три тезиса:
- Composable-функции описывают UI от данных: быстрые, идемпотентные, без побочных эффектов.
- Рекомпозиция пересоздаёт только изменившиеся части UI; состояние живёт в
remember/mutableStateOf, поворот экрана переживаетrememberSaveable. - Modifier — упорядоченная цепочка, где порядок важен; компоненты принимают
modifierпараметром.
Главное
Compose — это не «функции вместо XML», а смена парадигмы: UI описывает состояние, а не манипулирует виджетами. Объяснили эту мысль и подкрепили примером с remember — база закрыта.
Если чувствуете, что знаний по кускам много, а уверенно ответить на «как работает Compose» всё равно не можете — не нужно зубрить дальше. Напишите в Telegram, разберём вашу ситуацию и составим план подготовки.