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 се дефинира веднъж и поддържа добавяне на нови полета без загуба на стари данни. Ако в нова версия на приложението се добави поле със стойност по подразбиране, старият файл ще бъде правилно десериализиран — обратна съвместимост е вградена в протокола.

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 или ръчно да се управляват coroutines. За 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 схема и автоматична миграция
  • Миграция от SharedPreferences към DataStore е вградена чрез SharedPreferencesMigration
  • Безопасност на нишки — всички операции са асинхронни, блокирането на UI е изключено
  • Реактивност — Flow уведомява абонатите при всяка промяна на данни
  • Препоръка — използвайте DataStore във всички нови Android проекти, мигрирайте съществуващите при работа с настройки

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

IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.

Обсъдете проекта

Прочетете също