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 или ручно управљати корутинама. За 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. године. Саветоваћемо вас и предложити најбоље решење.

Разговарајте о пројекту

Прочитајте такође