Введение
«Что такое ресурсы в Android?» — вопрос-фильтр. Кандидат, который отвечает «это картинки и тексты в папке res», и кандидат, который объясняет, как Android выбирает нужный ресурс под конкретное устройство, — это два разных уровня.
Ресурсы — это дополнительные файлы и статическое содержимое, которое использует код: строки интерфейса, изображения, цвета, размеры, анимации, шрифты. Формальное определение звучит сухо, но за ним стоит важный принцип: ресурсы выносятся из кода, чтобы ими можно было управлять независимо — и чтобы одно и то же приложение подстраивалось под разные устройства.
На собеседованиях тема ресурсов всплывает не «в лоб», а в десятке смежных вопросов: почему нельзя захардкодить строку, зачем нужен класс R, что такое drawable-hdpi, почему приложение падает на устройстве с другим языком. Если вы понимаете механику — отвечаете на всё это без шпаргалок.
Разберём по порядку: что такое ресурсы и зачем они нужны, как устроен res/, как работают квалификаторы и как обратиться к ресурсу из кода и XML.
Что такое ресурсы и почему их выносят из кода
Ресурс — это любое статическое содержимое, которое отделено от кода: строки, картинки, анимации, стили, шрифты, произвольные XML. Официальная документация прямо говорит: всегда выносите ресурсы приложения из кода, чтобы управлять ими независимо и предоставлять альтернативные версии под конкретные конфигурации устройств.
Что это значит на практике? Строку «Подтвердить» вы не пишете прямо в Kotlin-файле — вы объявляете её в strings.xml и обращаетесь по имени. Картинку не кладёте в папку с кодом — кладёте в res/drawable. Зачем так делать, если можно проще?
Типичная ошибка новичка: хардкодить строки и цвета прямо в коде. Линтер подсветит это как HardcodedText, но главная проблема глубже: захардкоженную строку нельзя перевести, нельзя изменить в одном месте, и она не подстроится под конфигурацию устройства. Ресурсы — это разделение «данные» и «логика».
Вынос ресурсов из кода даёт три вещи, о которых стоит сказать на собеседовании:
- Независимое управление. Строки, цвета, размеры меняются в одном месте, без правки логики.
- Локализация. Один и тот же код + разные strings.xml под разные языки.
- Адаптивность. Один и тот же идентификатор ресурса, но разные файлы под разные плотности экрана, ориентацию и ночной режим.
Именно третий пункт — то, что отличает «знаю, что папка есть» от «понимаю, как работает». Дальше разберём, как это устроено.
res/: структура и типы ресурсов
Все ресурсы лежат в каталоге res/ проекта, и каждый тип — в отдельной подпапке. Имя подпапки не произвольное: оно определяет, как ресурс компилируется и как к нему обращаться.
Вот базовые каталоги из документации:
- drawable/ — растровые файлы (PNG, .9.png, JPG, GIF) и XML-графика: формы, state list, векторные картинки.
- mipmap/ — иконки запуска под разные плотности экрана. Отдельный каталог, потому что лаунчер запрашивает иконку в большем разрешении, чем drawable.
- values/ — «простые значения»: строки, цвета, размеры, стили. Один файл может содержать сколько угодно ресурсов: каждый дочерний элемент
<resources>определяет один ресурс.<string>создаёт R.string,<color>— R.color. - raw/ — произвольные файлы, которые нужны как есть; читаются через
openRawResource()какInputStream. - xml/ — произвольные XML-файлы конфигурации, читаются через
getXml(). - font/ — файлы шрифтов TTF, OTF, TTC или XML с
<font-family>. - layout/, menu/, anim/, color/ — разметки, меню, анимации, списки цветов.
Ключевой факт
В values/ каждый ресурс определяется отдельным XML-элементом — поэтому файл может называться как угодно, а типы ресурсов внутри перемешиваться. А вот в drawable/ или layout/ один файл — это один ресурс с именем файла.
Правило, которое стоит запомнить: файлы напрямую в res/ класть нельзя — только в подкаталоги, иначе ошибка компиляции. А если ресурсу нужен доступ по имени файла и иерархии, а не по идентификатору — это путь в assets/, а не в res/: файлы в assets/ не получают идентификатора и читаются только через AssetManager.
Квалификаторы: один ресурс — много версий
Самое интересное начинается здесь. Ресурсы по умолчанию лежат в «чистых» каталогах: res/values, res/drawable. А альтернативные версии — в каталогах с именем вида <имя>-<квалификатор>: values-en, drawable-hdpi, drawable-night.
Квалификатор описывает конфигурацию устройства, для которой предназначен ресурс: язык и регион, плотность экрана, ориентация, ночной режим, размер экрана. Например:
res/
values/ strings.xml // по умолчанию
values-en/ strings.xml // английский
values-pl/ strings.xml // польский
drawable/ icon.png // по умолчанию
drawable-hdpi/ icon.png // для высокой плотности
drawable-night/ background.png // для ночного режимаОбратите внимание: имена файлов одинаковые, а содержимое — своё под каждую конфигурацию. Идентификатор ресурса при этом один и тот же, а какую версию взять — Android решает в рантайме по текущей конфигурации устройства.
Два правила, на которых сыплются кандидаты:
- Порядок квалификаторов фиксирован. Если их несколько — они идут строго в порядке таблицы из документации. Нарушили порядок — ресурс будет проигнорирован.
- Ресурсы по умолчанию обязательны. Если строка есть только в values-en, а система на устройстве — русская, приложение упадёт с ResourceNotFound. Ресурс по умолчанию — страховка от любой непредусмотренной конфигурации.
Частая ошибка на собеседовании: «Ну я добавил значения в values-en, зачем ещё values?». Затем, что Android не обязан угадать. Нет подходящей альтернативы — нет ресурса. Документация подчёркивает: всегда указывайте ресурсы по умолчанию, иначе приложение аварийно завершается.
Доступ к ресурсам: R-класс, @string и @drawable
Идентификатор ресурса — это всегда связка «тип + имя». На этапе компиляции инструмент aapt генерирует класс R: для каждого типа — подкласс (R.string, R.drawable, R.color), внутри — статическое целое для каждого ресурса (R.drawable.icon).
Из кода обращение выглядит так:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val title = getString(R.string.app_title)
val icon = ContextCompat.getDrawable(this, R.drawable.ic_logo)
}
}В XML-файлах — тот же механизм, но в синтаксисе @тип/имя:
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/app_title"
android:drawableStart="@drawable/ic_logo" />Три детали, которые показывают глубину:
- Через Context. Вне UI-иерархии — в ViewModel, Repository, Service — ресурсы разрешаются через Context:
getString(),getColor()и другие методы. Это ещё одна причина, почему Context нужно прокидывать в низкие слои аккуратно. - Ресурсы платформы. Можно ссылаться на системные ресурсы через
android.R.drawable.ic_menu_info_details— так берут стандартные иконки и стили. - Compose работает с теми же идентификаторами.
stringResource(R.string.app_title)иpainterResource(R.drawable.ic_logo)— безопасный доступ с учётом рекомпозиции.
- Ресурсы — статическое содержимое, вынесенное из кода: строки, картинки, цвета, шрифты.
- Каждый тип — своя подпапка в res/; в values/ один файл может содержать много ресурсов.
- Квалификаторы в имени каталога подбирают версию под конфигурацию устройства, порядок квалификаторов фиксирован.
- Идентификатор генерируется в R-класс: из кода — R.string.name, из XML — @string/name.
- Ресурсы по умолчанию обязательны, иначе — ResourceNotFound.
Почему это важно для собеседования? Потому что вопросы про ресурсы — это проверка системного мышления. Кандидат, который связывает res/, R-класс, квалификаторы и конфигурацию устройства в одну картину, отвечает на смежные темы — плотность экрана, локализацию, dp — без натуги.
Когда я провожу собеседования, я спрашиваю про ресурсы именно так: не «что лежит в res/», а «что произойдёт, если строки есть только в values-en». И это отсеивает тех, кто заучил структуру, от тех, кто понимает механику. Если такие пробелы накапливаются — это сигнал, что нужна системная подготовка, а не заучивание новых вопросов.
Разбираете платформенные темы перед собеседованием?
Ресурсы, жизненные циклы, интенты — это фундамент, на котором держатся ответы уровня middle. На мок-собеседованиях тренируем их до уверенных ответов.
Итоги
Ресурсы в Android — это не «папка с картинками», а продуманная система: статическое содержимое отделено от кода, каждый тип лежит в своём каталоге res/, а альтернативные версии под конфигурации устройств описываются квалификаторами в именах каталогов.
Три тезиса, которые стоит унести с собой:
- Идентификатор — это «тип + имя». Класс R генерируется на этапе компиляции: из кода —
R.string.name, из XML —@string/name. Один идентификатор — разные версии ресурса. - Квалификаторы — это адаптивность. values-en, drawable-hdpi, drawable-night: Android выбирает лучшую версию в рантайме. Но без ресурсов по умолчанию приложение падает.
- Ресурсы — часть архитектуры. Они причина, по которой Context прокидывают аккуратно, и причина, по которой локализация и адаптация под устройства не превращаются в простыню условий.
Если на собеседовании застряли на вопросе «что будет, если...» про ресурсы — это не стыдно, это закрывается практикой за пару вечеров. Но если таких «если» накопилось много по разным темам, лучше не идти на собеседование вслепую. Напишите в Telegram — разберём вашу ситуацию и составим план подготовки к уверенному middle.