DataStore: что это, основы хранения данных и замена SharedPreferences

Автор: IT Sectr Опубликовано: 2026-03-12 Время чтения: 9 мин

DataStore — компонент из библиотеки Jetpack, предназначенный для хранения небольших объёмов данных в Android-приложениях. В отличие от SharedPreferences, он работает асинхронно и гарантирует согласованность данных при конкурентном доступе. По данным Google, 2024, DataStore использует Kotlin Coroutines и Flow, что делает его безопасным для главного потока и подходящим для реактивных архитектур.

Главное

  • DataStore — замена SharedPreferences с асинхронным API и типами данных через Protocol Buffers
  • Preferences DataStore — простой Key-Store с чтением через Flow и транзакциями
  • Proto DataStore — типизированное хранилище с автоматическими миграциями схемы
  • SharedPreferences — синхронное API, блокирующее UI-поток при больших объёмах
  • Миграция выполняется через специальный интерфейс SharedPreferencesMigration без потери данных

Что такое DataStore?

DataStore — решение от Google для локального хранения данных в Android, представленное в 2020 году как альтернатива SharedPreferences. Он поддерживает два режима: Preferences DataStore (простые пары ключ-значение) и Proto DataStore (типизированная схема на основе Protocol Buffers).

Основное преимущество — полная асинхронность: все операции чтения возвращают Flow из Kotlin Coroutines, а запись выполняется в coroutine-контексте. Это исключает блокировку главного потока, которая была типичной проблемой SharedPreferences при работе с большими объёмами данных.

DataStore гарантирует атомарность операций: конкурентные записи не приводят к потере данных благодаря транзакционной модели. Если два компонента одновременно изменяют одно и то же значение, DataStore корректно обрабатывает конфликт через механизм compare-and-swap.

По данным Google I/O 2023, DataStore используется в 40% новых проектов на Android, и Google рекомендует мигрировать с SharedPreferences во всех приложениях, где требуется стабильность хранения настроек.

Архитектура DataStore

В основе DataStore лежит SingleProcessDataStore — реализация, работающая в рамках одного процесса. Она использует файловое хранилище с блокировками на уровне файла: при записи данных файл блокируется, что предотвращает повреждение при конкурентном доступе.

DataStore автоматически обрабатывает ошибки десериализации: если файл повреждён, он возвращает значение по умолчанию и перезаписывает файл. Это поведение настраивается через corruptionHandler, который можно задать при создании DataStore.

Проблемы SharedPreferences, которые решает DataStore

SharedPreferences страдает от трёх фундаментальных проблем: синхронное чтение с диска на главном потоке, отсутствие гарантий атомарности при конкурентных записях и невозможность реактивного отслеживания изменений. DataStore решает все три: Flow для наблюдения, файловая блокировка для атомарности и асинхронное API для безопасности потоков.

Как работает DataStore в Android?

DataStore хранит данные в файлах на внутреннем хранилище устройства. Preferences DataStore использует формат файла, аналогичный SharedPreferences, но с дополнительными метаданными для проверки целостности. Proto DataStore использует бинарный формат Protocol Buffers, что уменьшает размер файла и ускоряет сериализацию.

При чтении данных DataStore загружает весь файл в память однократно, после чего подписчики получают актуальное состояние через Flow. Изменения транслируются всем активным подписчикам автоматически — не требуется ручная регистрация listener-ов, как в SharedPreferences.

Принцип работы Preferences DataStore

Preferences DataStore использует встроенный механизм сериализации на основе Map. Каждая запись — пара строки и примитивного типа (Int, Boolean, Float, Long, String, Set). Данные хранятся в XML-файле, аналогичном SharedPreferences, но с атомарной записью через файловую блокировку.

Пример создания Preferences DataStore: расширение preferencesDataStore на Context создаёт синглтон с именем файла. При повторных вызовах возвращается тот же экземпляр — это исключает дублирование файлов и путаницу с разными экземплярами хранилища.

Принцип работы Proto DataStore

Proto DataStore требует определения схемы данных через .proto-файл и компиляции с помощью protobuf-плагина. Сгенерированный Java-класс используется как единственная точка входа для всех полей — это исключает опечатки в ключах, типичные для SharedPreferences.

Схема Proto DataStore задаётся один раз и поддерживает добавление новых полей без потери старых данных. Если в новой версии приложения добавить поле с default-значением, старый файл будет корректно десериализован — обратная совместимость встроена в протокол.

Preferences DataStore и Proto DataStore: сравнение

Выбор между Preferences DataStore и Proto DataStore зависит от сложности данных и требований к типизации. Оба варианта асинхронны и транзакционны, но различаются по уровню type-safety и производительности сериализации.

ХарактеристикаPreferences DataStoreProto DataStore
ТипизацияСлабая (ключ-значение)Строгая (сгенерированный класс)
СериализацияXML (встроенная)Protocol Buffers (protobuf)
Размер файлаБольшой (читаемый XML)Малый (бинарный)
СложностьНизкая (без .proto)Средняя (нужен .proto)
Миграция схемыНет схемыАвтоматическая (proto)
СовместимостьSharedPreferences (через миграцию)Только Proto DataStore

Когда выбирать Preferences DataStore

Preferences DataStore подходит для простых настроек: флаги включения функций, строка токена авторизации, число запусков приложения. Если данных мало (до 10–15 ключей) и они не требуют строгой схемы — Preferences DataStore даёт минимальный порог входа без подключения protobuf-плагина.

Когда выбирать Proto DataStore

Proto DataStore оправдан, когда структура данных сложная или может меняться между версиями приложения. Например, настройки профиля пользователя или конфигурация A/B-тестов с 20+ полями. Protobuf даёт строгую типизацию и автоматические миграции, что исключает ошибки времени выполнения из-за несовпадения ключей.

Как мигрировать с SharedPreferences на DataStore

Google предоставляет встроенный механизм миграции через класс SharedPreferencesMigration. Миграция выполняется однократно при первом запуске после обновления приложения: DataStore читает данные из SharedPreferences, записывает их в свой формат и помечает миграцию завершённой.

Миграция поддерживает кастомные трансформации: если в SharedPreferences ключи не совпадают с желаемыми ключами DataStore, можно задать функцию преобразования через SharedPreferencesMigration. Это позволяет переименовывать ключи и менять типы данных в процессе миграции.

Пошаговая миграция

Первым шагом добавьте DataStore в build.gradle и создайте экземпляр DataStore с миграцией: SharedPreferencesMigration принимает имя SharedPreferences-файла и набор ключей, которые нужно перенести. Вторым шагом удалите весь код, работающий через SharedPreferences, и замените его вызовами DataStore. Третьим — протестируйте миграцию: при первом запуске данные должны появиться в DataStore, а старый SharedPreferences-файл — перестать использоваться.

kotlin
val Context.dataStore by preferencesDataStore(
    name = "settings",
    produceMigrations = { context ->
        listOf(
            SharedPreferencesMigration(context, "old_prefs")
        )
    }
)

Примеры использования DataStore в коде

DataStore легко интегрируется в существующий проект. Ниже приведены практические примеры для Preferences DataStore и Proto DataStore — оба демонстрируют чтение, запись и реактивное наблюдение за данными.

Preferences DataStore: чтение и запись настроек

В этом примере Preferences DataStore хранит три настройки: тёмную тему, имя пользователя и количество запусков. Чтение выполняется через расширение .data, которое возвращает Flow. Запись — через suspend-функцию .edit, гарантирующую атомарность изменений.

kotlin
val Context.settingsDataStore by preferencesDataStore(name = "settings")

val isDarkMode: Flow<Boolean> = settingsDataStore.data
    .map { preferences ->
        preferences[booleanPreferencesKey("dark_mode")] ?: false
    }

suspend fun toggleDarkMode() {
    settingsDataStore.edit { prefs ->
        val current = prefs[booleanPreferencesKey("dark_mode")] ?: false
        prefs[booleanPreferencesKey("dark_mode")] = !current
    }
}

Proto DataStore: схема и использование

Proto DataStore требует определения .proto-файла. После компиляции создаётся класс UserSettings, который используется для чтения и записи. Миграции версий схемы описываются в том же .proto-файле и применяются автоматически.

kotlin
// user_preferences.proto
syntax = "proto3";

message UserPreferences {
    string display_name = 1;
    int32 notification_count = 2;
    bool notifications_enabled = 3;
}

// Чтение из DataStore
val userPreferencesFlow: Flow<UserPreferences> =
    protoDataStore.data

// Запись новых значений
suspend fun updateDisplayName(name: String) {
    protoDataStore.updateData { prefs ->
        prefs.toBuilder()
            .setDisplayName(name)
            .build()
    }
}

Реактивное наблюдение за изменениями

DataStore интегрируется с архитектурой MVVM через ViewModel. Flow из DataStore собирается через .stateIn и используется в UI. При каждом изменении данных UI обновляется автоматически — не нужны ручные обновления или LiveData.

kotlin
class SettingsViewModel(
    private val dataStore: DataStore<Preferences>
) : ViewModel() {

    val uiState: StateFlow<SettingsUiState> =
        dataStore.data
            .map { prefs ->
                SettingsUiState(
                    isDarkMode = prefs[booleanPreferencesKey("dark_mode")] ?: false,
                    counter = prefs[intPreferencesKey("launch_count")] ?: 0
                )
            }
            .stateIn(
                scope = viewModelScope,
                started = SharingStarted.WhileSubscribed(5000),
                initialValue = SettingsUiState()
            )
}

Часто задаваемые вопросы

Чем DataStore лучше SharedPreferences?

DataStore работает асинхронно (не блокирует UI-поток), поддерживает конкурентный доступ через транзакции и позволяет реактивно подписываться на изменения через Flow. SharedPreferences — синхронное API с риском ANR при больших объёмах данных и без встроенной поддержки реактивности.

Можно ли использовать DataStore с Java?

DataStore написан на Kotlin и требует Kotlin Coroutines. Использовать его из Java можно, но это неудобно: придётся создавать обёртки с CompletableFuture или вручную управлять корутинами. Для Java-проектов Google рекомендует оставить SharedPreferences или добавить Kotlin в модуль.

DataStore подходит для хранения больших объёмов данных?

DataStore загружает весь файл в память при чтении, поэтому не подходит для хранения списков или больших объектов. Для таких сценариев используйте Room или SQLite. DataStore оптимизирован для настроек и небольших структурированных данных — до сотен килобайт.

Как обработать ошибку повреждённого файла DataStore?

При создании DataStore можно передать corruptionHandler — функцию, которая вызывается при повреждении файла. По умолчанию DataStore выбрасывает исключение CorruptionException. В corruptionHandler можно вернуть пустые данные, после чего DataStore перезапишет файл корректным состоянием.

Proto DataStore требует обязательного .proto-файла?

Да, Proto DataStore требует определения схемы в .proto-файле и подключения плагина protobuf-gradle-plugin. Если проект небольшой и данные простые, проще использовать Preferences DataStore — он не требует дополнительной конфигурации сборки.

Итоги

  • DataStore — современная замена SharedPreferences, работающая с Kotlin Coroutines и Flow
  • Preferences DataStore — простой ключ-значение без схемы, подходит для настроек
  • Proto DataStore — типизированное хранилище с protobuf-схемой и автo-миграцией
  • Миграция с SharedPreferences встроена в DataStore через SharedPreferencesMigration
  • Безопасность потоков — все операции асинхронны, блокировка UI исключена
  • Реактивность — Flow уведомляет подписчиков при каждом изменении данных
  • Рекомендация — используйте DataStore во всех новых проектах Android, мигрируйте существующие при работе с настройками

Мы разработаем мобильное приложение под ключ

IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.

Обсудить проект

Читайте также