Вопрос, который теперь задают вместо 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.

kotlin
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 при чтении или записи.

kotlin
SettingsDataStore.kt

val Context.settingsDataStore: DataStore<Preferences> by preferencesDataStore(
    name = "settings"
)

Ключи задаются типизированными функциями: stringPreferencesKey("user_name"), intPreferencesKey("counter"), booleanPreferencesKey("dark_theme") и так далее. В отличие от SharedPreferences, где ключ — просто строка, здесь тип ключа зашит в тип значения: положить Int по ключу, созданному как строковый, не получится без явного приведения.

Чтение — это работа с Flow:

kotlin
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 строк (список поддерживаемых типов указан в документации класса).

kotlin
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, пришлите код, разберём вашу ситуацию и составим план.