Введение
«Почему при переключении тёмной темы пересоздаётся Activity?» — вопрос, на котором отсеивается половина кандидатов. Тёмная тема кажется простой, но за ней стоит целый механизм: DayNight-темы, ночные ресурсы и конфигурационные изменения.
Тёмная тема перестала быть «фичей для гиков»: начиная с Android 10 (API level 29) она встроена в систему, и пользователи включают её на уровне ОС. Это значит, что любое приложение без поддержки тёмной темы выглядит как белый прямоугольник в тёмном интерфейсе. А на собеседованиях вопросы про тёмную тему — это проверка того, как вы работаете с ресурсами и тем, а не зазубрили ли вы названия атрибутов.
В этой статье разберём по порядку: что такое тёмная тема и зачем она нужна, как подключить её через DayNight и values-night, как правильно переключать режим в приложении и на какие подводные камни чаще всего натыкаются кандидаты.
Что такое тёмная тема и почему она важна
Тёмная тема — это не «инверсия цветов», а отдельный набор ресурсов, который подгружается в зависимости от ночного режима устройства.
Официальная документация выделяет три причины, по которым тёмная тема важна: она экономит заряд на экранах, где это поддерживается, улучшает видимость для пользователей с низким зрением и снижает нагрузку на глаза в условиях слабого освещения. Система умеет включать её тремя способами: через настройки (Settings > Display > Theme), через плитку в шторке уведомлений и вместе с режимом экономии заряда на устройствах Pixel.
Частая ошибка на собеседовании: кандидат говорит «тёмная тема — это просто тёмные цвета вместо светлых». Нет. Тёмная тема — это переключение набора ресурсов и атрибутов темы. Приложение не «перекрашивается» автоматически: для этого используется либо DayNight-тема, либо ночные альтернативные ресурсы.
Для разработчика тёмная тема сводится к двум вещам. Первая — тема, которая умеет переключаться между светлым и тёмным вариантом (это DayNight). Вторая — ресурсы, которые система подставляет в зависимости от ночного режима: директории вроде values-night/ и drawable-night/. Квалификатор night существует с API level 8, так что он не привязан к Android 10 — ночная тема системы лишь дала пользователям удобный переключатель.
Ещё один момент, который стоит держать в голове: официальная документация прямо рекомендует избегать хардкода цветов и использовать атрибуты темы — например, ?android:attr/textColorPrimary (почти чёрный в светлой теме и почти белый в тёмной) или ?attr/colorControlNormal для иконок. Это ключ ко всей статье: тёмная тема работает ровно потому, что вы не зашиваете цвета в код.
DayNight и values-night: как включить тёмную тему
Вся магия начинается с одной строки в themes.xml: родитель Theme.Material3.DayNight.
Чтобы приложение поддерживало тёмную тему, тема должна наследоваться от DayNight-варианта: Theme.AppCompat.DayNight, Theme.MaterialComponents.DayNight или Theme.Material3.DayNight. Так тема привязывается к системным флагам ночного режима и получает тёмный вариант по умолчанию, когда он включён.
<style name="Theme.MyApp" parent="Theme.Material3.DayNight">
<item name="colorPrimary">@color/brand</item>
<item name="colorOnPrimary">@color/white</item>
</style>Дальше вступают в игру альтернативные ресурсы. Классический пример — цвета: одно имя ресурса, два значения в зависимости от режима.
<resources>
<color name="brand">#6750A4</color>
<color name="surface">#FEF7FF</color>
</resources><resources>
<color name="brand">#D0BCFF</color>
<color name="surface">#141218</color>
</resources>Почему это спрашивают на собеседовании
values-night — это не «особая папка тёмной темы», а обычный механизм альтернативных ресурсов с квалификатором night (работает с API level 8). Если ресурс объявлен только в values-night/ и нигде больше, приложение упадёт на устройстве без ночного режима — об этом любят спрашивать, и к этому мы ещё вернёмся.
Та же логика работает для любых типов ресурсов: иконки в drawable-night/, стили в values-night/themes.xml, даже строки. Система сама выбирает нужный набор по текущей конфигурации устройства — вам не нужно писать ни строчки кода. На собеседовании стоит проговорить это вслух: «тёмная тема — это переключение ресурсов через квалификатор night, а тема DayNight лишь привязывает приложение к системному ночному режиму».
Переключение темы внутри приложения
Три режима: светлый, тёмный и «как в системе». Последний — рекомендованный вариант по умолчанию.
Если вы даёте пользователю выбор темы в настройках приложения, варианты обычно такие: Light, Dark и System default. В терминах AppCompat это MODE_NIGHT_NO, MODE_NIGHT_YES и MODE_NIGHT_FOLLOW_SYSTEM. В коде переключение выглядит так:
private fun applyDarkMode() {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
}Здесь есть два нюанса, которые часто всплывают на собеседованиях. Первый: с версии AppCompat 1.1.0 setDefaultNightMode() автоматически пересоздаёт запущенные Activity — поэтому состояние экрана нужно хранить не в переменных, а там, где оно переживает пересоздание (например, во ViewModel или savedInstanceState). Второй: начиная с API level 31 система рекомендует использовать UiModeManager.setApplicationNightMode(), чтобы тема подхватывалась ещё на сплэш-экране.
private fun applyDarkMode() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
getSystemService(UiModeManager::class.java)
.setApplicationNightMode(UiModeManager.MODE_NIGHT_YES)
} else {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
}
}Типичный промах: кандидат уверенно рассказывает про setDefaultNightMode(), но не может объяснить, почему Activity пересоздаётся. Ответ: смена темы — это конфигурационное изменение uiMode. Activity пересоздаётся так же, как при повороте экрана. Если нужно обработать смену вручную, можно объявить android:configChanges="uiMode" и переопределить onConfigurationChanged() — но это частный случай, а не стандарт.
Проверить текущий режим можно через Configuration — этот код есть в официальной документации, и его стоит помнить:
fun isDarkTheme(configuration: Configuration): Boolean {
val nightMode = configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
return nightMode == Configuration.UI_MODE_NIGHT_YES
}- DayNight-тема связывает приложение с системным ночным режимом.
values-night/— альтернативные ресурсы, которые система выбирает сама.MODE_NIGHT_FOLLOW_SYSTEM— рекомендуемый вариант по умолчанию.- Смена темы = изменение конфигурации
uiMode= пересоздание Activity. - На API 31+ для переключения используем
UiModeManager, на более старых —AppCompatDelegate.
Force Dark и подводные камни
Force Dark — это «костыль» Android 10: система сама инвертирует светлую тему, но полагаться на него нельзя.
Когда тёмная тема появилась в Android 10, Google добавила механизм Force Dark: приложение-«незнакомец» со светлой темой система автоматически переводит в тёмные цвета перед отрисовкой. Для этого нужно явно включить атрибут android:forceDarkAllowed="true" в теме Activity. Звучит удобно, но есть принципиальное ограничение: если ваша тема наследуется от DayNight, Force Dark не применяется — потому что переключение уже работает через ресурсы. Так что Force Dark — это временное решение для легаси, а не стратегия.
Главная ошибка на собеседовании: кандидат предлагает «реализовать тёмную тему» через Force Dark или через код вида if (dark) setBackgroundColor(black). Это антипаттерны: первый не работает с DayNight, второй — хардкод, который убивает всю идею ресурсов. Правильный путь — атрибуты темы и ночные ресурсы.
Из той же серии — классические грабли, перечисленные в документации: жёстко заданный белый фон иконки или текста, setTextColor(Color.BLACK) в коде, статичные по цвету drawable. В тёмной теме всё это превращается в нечитаемый UI. Решения два: использовать атрибуты темы (?attr/colorSurface, ?attr/colorOnSurface) или выносить значения в ресурсы с ночными альтернативами.
Способа включить тёмную тему системой
Настройки дисплея, плитка быстрых настроек и режим экономии заряда на Pixel. Приложение должно одинаково корректно выглядеть при каждом из них.
И ещё один нюанс из практики: уведомления и виджеты. Для уведомлений используйте системные шаблоны (например, MessagingStyle) — система сама применит нужную тему. А вот для своих виджетов на рабочем столе придётся проверить оба режима вручную. Это та самая конкретика, которая выделяет кандидата: знание документации + реальный опыт.
Кто я и как помогу подготовиться
Меня зовут Рустем Бикбулатов, я senior Android-разработчик и провожу технические собеседования. Помогаю Android-разработчикам с опытом 1–2 года переходить на уровень middle за 4–8 недель: закрываю пробелы в Kotlin, Coroutines, архитектуре и тренирую ответы на собеседованиях в Яндекс, Авито и других сильных компаниях.
Вопросы про темы, ресурсы и тёмную тему — это лишь верхушка айсберга: за ними стоят жизненный цикл, конфигурационные изменения и архитектура приложения. Именно такие связки интервьюеры и проверяют. За годы менторства в Яндекс Практикуме я провёл более 100 учеников и разборов — и почти всегда проблемы у кандидатов не в отдельных темах, а в отсутствии системной картины.
Нужна помощь в переходе на middle?
Бесплатная диагностика: разберу ваши пробелы и составлю план роста за 30–40 минут. Без обязательств — если менторство вам не подойдёт, честно скажу.
Итоги
Подведём итоги по тёмной теме:
- Тёмная тема — это ресурсы и тема, а не перекраска в коде. DayNight-тема привязывает приложение к системному режиму, а
values-night/,drawable-night/и другие ночные директории подставляют нужные значения автоматически. - Смена темы — это конфигурационное изменение
uiMode. Activity пересоздаётся, как при повороте экрана. Переключение делаем черезAppCompatDelegate.setDefaultNightMode()илиUiModeManager.setApplicationNightMode()на API 31+. - Хардкод цветов — главный враг.
setTextColor(Color.BLACK)и статичные по цвету иконки ломают тёмную тему. Используйте атрибуты темы и ночные ресурсы — это же ответ на вопрос «почему Activity пересоздаётся при смене темы».
Главное, что нужно запомнить
DayNight + ночные ресурсы + атрибуты темы вместо хардкода — это и есть ответ про тёмную тему. Если на собеседовании вы расскажете про пересоздание Activity через изменение конфигурации uiMode и про MODE_NIGHT_FOLLOW_SYSTEM как рекомендуемый дефолт — вы уже сильнее большинства кандидатов.
Если чувствуете, что такие связки между темами и жизненным циклом «плавают» в голове — не нужно зубрить дальше. Напишите в Telegram, разберём вашу ситуацию и составим план подготовки.