Вопрос, который теперь задают вместо SharedPreferences
«Вы ещё используете SharedPreferences? А почему не DataStore?» — этот вопрос звучит на собеседованиях всё чаще.
Несколько лет назад вопрос про хранение настроек был простым: рассказал про apply() и commit() — и достаточно. Сейчас Google официально рекомендует DataStore как замену SharedPreferences, и интервьюеры это проверяют. Кандидат, который в 2026 году пишет новый код на SharedPreferences и не может объяснить почему, теряет баллы.
DataStore — это библиотека Jetpack для хранения пар «ключ — значение» и типизированных объектов, построенная на корутинах и Flow. Звучит страшнее, чем есть на самом деле: за час практики вы разберётесь, а за статью — тем более. Разберём, как устроен DataStore, чем отличаются его две реализации и как перенести существующие настройки без потери данных.
Типичная ошибка кандидатов: отвечают «DataStore — это как SharedPreferences, только новый». Интервьюер ждёт большего: разницу между Preferences и Proto DataStore, понимание Flow и честный ответ, когда DataStore не подходит.
Почему SharedPreferences меняют на DataStore
Чтобы понять, зачем нужен DataStore, вспомним, чем болеет SharedPreferences: синхронное чтение, которое парсит файл целиком, перезапись всего файла при каждом изменении, отсутствие гарантий при работе с главным потоком. DataStore создан, чтобы эти болезни вылечить.
Официальная документация формулирует так: DataStore использует сопрограммы Kotlin и Flow для асинхронного, согласованного и транзакционного хранения данных — это руководство по Jetpack DataStore на developer.android.com. Разберём, что это значит на практике:
- [ ] Асинхронность: чтение и запись не блокируют поток — никаких сюрпризов на главном потоке.
- [ ] Согласованность: вы читаете только то, что уже записано на диск, без промежуточных состояний.
- [ ] Транзакционность:
updateData— атомарная операция «чтение-изменение-запись», сбой не оставит файл в половине обновлённом виде. - [ ] Реактивность: данные — это
Flow, UI подписывается и обновляется сам.
У DataStore две реализации, и путать их нельзя — это отдельный вопрос на собеседовании:
- Preferences DataStore — пары «ключ — значение», как в SharedPreferences. Без предопределённой схемы, без типобезопасности, API похож на старый. Для настроек приложения хватает с головой.
- Proto DataStore — типизированные объекты на основе Protocol Buffers. Нужна схема данных и
Serializer, зато компилятор проверяет типы. Для сложных структур.
Текущая версия androidx.datastore:datastore-preferences — 1.2.1. Зависимость добавляется одной строкой в Gradle.
build.gradle.kts (module)
dependencies {
// Preferences DataStore — API в стиле SharedPreferences
implementation("androidx.datastore:datastore-preferences:1.2.1")
}Preferences DataStore: ключи и Flow
Начинают обычно с Preferences DataStore — он ближе всего к SharedPreferences. Первое, что бросается в глаза: вместо getSharedPreferences() — делегат на верхнем уровне файла. Это не просто украшение: экземпляр DataStore должен быть один на весь процесс. Документация прямо предупреждает: если создать два экземпляра для одного файла, DataStore выбросит IllegalStateException при чтении или записи.
SettingsDataStore.kt
val Context.settingsDataStore: DataStore<Preferences> by preferencesDataStore(
name = "settings"
)Ключи задаются типизированными функциями: stringPreferencesKey("user_name"), intPreferencesKey("counter"), booleanPreferencesKey("dark_theme") и так далее. В отличие от SharedPreferences, где ключ — просто строка, здесь тип ключа зашит в тип значения: положить Int по ключу, созданному как строковый, не получится без явного приведения.
Чтение — это работа с Flow:
SettingsRepository.kt
class SettingsRepository(private val context: Context) {
val userName: Flow<String> = context.settingsDataStore.data.map { prefs ->
prefs[Keys.USER_NAME] ?: ""
}
suspend fun saveUserName(name: String) {
context.settingsDataStore.edit { prefs ->
prefs[Keys.USER_NAME] = name
}
}
private object Keys {
val USER_NAME = stringPreferencesKey("user_name")
}
}Запись через edit {} или updateData {} — suspend-функции, поэтому вызываются из корутины. Это честный ответ на вопрос «почему DataStore асинхронный»: API вынуждает вас думать о потоке, а не надеяться, что «само пройдёт».
Типичная ошибка: кандидат называет DataStore «реактивным хранилищем», но не может написать data.map { it[KEY] ?: default }. На мок-собеседовании я прошу написать репозиторий настроек — и сразу видно, читал ли человек код DataStore или только обзор.
В архитектуре DataStore живёт в слое данных: репозиторий отдаёт Flow, ViewModel превращает его в StateFlow, UI подписывается. Прятать вызовы DataStore в Composable-функции — антипаттерн, о котором предупреждает официальная документация.
Миграция с SharedPreferences
Вопрос, который чаще всего задают в продакшене: «У нас три года настроек в SharedPreferences — что с ними будет?» Ответ: DataStore умеет мигрировать автоматически, и данные не потеряются.
Для этого при создании DataStore передаётся список миграций. SharedPreferencesMigration переносит значения из старого файла в новый: поддерживаются базовые типы — boolean, float, int, long, string и set строк (список поддерживаемых типов указан в документации класса).
SettingsDataStore.kt
private val Context.settingsDataStore: DataStore<Preferences> by preferencesDataStore(
name = "settings",
produceMigrations = { context ->
listOf(
SharedPreferencesMigration(
context,
sharedPreferencesName = "com.example.myapp.settings"
)
)
}
)Миграция выполняется один раз при первом чтении: старые значения переносятся, а если SharedPreferences после переноса пуст — файл удаляется. Ключи, которых нет в новом хранилище, просто переносятся «как есть» — поэтому важно, чтобы имена ключей совпадали.
- [ ] Добавляете
SharedPreferencesMigrationвproduceMigrations. - [ ] Указываете имя старого файла SharedPreferences.
- [ ] Первый запуск: значения переносятся автоматически при первом чтении.
- [ ] Дальше приложение работает только с DataStore.
Ещё один момент, о котором мало кто знает: файлы DataStore живут в каталоге files/datastore/ с расширением .preferences_pb и по умолчанию попадают в резервное копирование Android. Для чувствительных настроек стоит исключать их из бэкапа отдельными правилами.
Когда DataStore не нужен
DataStore — не серебряная пуля, и честный ответ об этом на собеседовании — плюс. Документация прямо говорит: если нужны большие или сложные наборы данных, частичные обновления или ссылочная целостность — берите Room. DataStore не умеет ни частичных обновлений, ни связей между данными, он рассчитан на небольшие объёмы.
Практическое правило, которое я советую кандидатам:
SharedPreferences / DataStore — для настроек и небольших состояний: тема, язык, флаги фич. Room — для бизнес-данных: списки, профили, всё, что нужно фильтровать и обновлять. Сеть — не хранилище вообще, а источник; кэш — это отдельная задача.
И ещё одно важное «когда нет»: если у вас старый проект, который работает, и миграция не приносит пользы — не нужно менять всё подряд. DataStore оправдан в новом коде и там, где SharedPreferences реально мешает: блокирует главный поток, страдает от больших файлов, нужна реактивность.
Опыт, который не прочитаешь в доках
Меня зовут Рустем Бикбулатов, я senior Android-разработчик и ментор Яндекс Практикума. Провожу технические собеседования, за моими плечами более ста учеников и разборов. На разборах кода я часто вижу одно и то же: разработчики с опытом один-два года знают API по отдельности, но не видят, как они соединяются — DataStore, Flow, ViewModel, репозиторий. Это нормально, это закрывается практикой под руководством, а не зубрёжкой.
Разберу ваш код и скажу, что мешает росту
Бесплатный разбор: пришлите код или резюме — посмотрю и скажу, что мешает перейти на следующий грейд.
Итоги
Соберём ответ на вопрос «Что такое DataStore и зачем он нужен?» — такого ответа достаточно, чтобы закрыть тему на собеседовании:
- DataStore — официальная замена SharedPreferences на корутинах и Flow: асинхронное, согласованное и транзакционное хранение пар «ключ — значение» или типизированных объектов.
- Две реализации: Preferences DataStore для настроек без схемы и Proto DataStore для типизированных данных со схемой; ключи типизированные, чтение — через
Flow, запись — через suspend-функцииedit/updateData. - Миграция встроена:
SharedPreferencesMigrationпереносит старые настройки при первом запуске, а для больших данных есть Room — DataStore не про это.
Главное, что нужно запомнить
DataStore выбирают не потому, что «SharedPreferences плохой», а потому что асинхронность, транзакции и Flow решают реальные проблемы: блокировку главного потока, перезапись файла целиком и отсутствие реактивности. Скажите это на собеседовании — и ответ будет звучать как понимание, а не как заученный факт.
Если на разборе вашего кода выяснится, что хранилище выбрано неправильно, — не страшно, это поправимо. Напишите в Telegram, пришлите код, разберём вашу ситуацию и составим план.