DataStore — a Jetpack könyvtár egyik komponense, amely kis mennyiségű adat tárolására szolgál Android alkalmazásokban. A SharedPreferences-szel ellentétben aszinkron módon működik, és garantálja az adatok konzisztenciáját konkurens hozzáférés esetén. A Google, 2024 adatai szerint a DataStore Kotlin Coroutines-t és Flow-t használ, ami biztonságossá teszi a főszál számára és alkalmassá a reaktív architektúrákhoz.
Főbb pontok
DataStore — egy Google megoldás helyi adattárolásra Androidban, amelyet 2020-ban mutattak be a SharedPreferences alternatívájaként. Két módot támogat: Preferences DataStore (egyszerű kulcs-érték párok) és Proto DataStore (Protocol Buffers alapú típusos séma).
A fő előny — teljes aszinkronitás: minden olvasási művelet Flow-t ad vissza a Kotlin Coroutines-ból, az írás pedig coroutine kontextusban történik. Ez kiküszöböli a főszál blokkolását, ami a SharedPreferences tipikus problémája volt nagy adatmennyiségek esetén.
A DataStore garantálja a műveletek atomitását: a konkurens írások a tranzakciós modellnek köszönhetően nem vezetnek adatvesztéshez. Ha két komponens egyidejűleg módosítja ugyanazt az értéket, a DataStore a compare-and-swap mechanizmuson keresztül helyesen kezeli a konfliktust.
A Google I/O 2023 adatai szerint a DataStore-t az Android új projektjeinek 40%-ában használják, és a Google a SharedPreferences-ről való migrációt ajánlja minden olyan alkalmazásban, ahol a beállítások tárolásának stabilitása szükséges.
A DataStore alapjában a SingleProcessDataStore áll — egy implementáció, amely egyetlen folyamaton belül működik. Fájltárolót használ fájlszintű zárolásokkal: adatírás közben a fájl zárolásra kerül, ami megakadályozza a sérülést konkurens hozzáférés esetén.
A DataStore automatikusan kezeli a deszerializációs hibákat: ha a fájl sérült, visszaadja az alapértelmezett értéket és felülírja a fájlt. Ez a viselkedés a corruptionHandler segítségével konfigurálható, amely a DataStore létrehozásakor állítható be.
SharedPreferences három alapvető problémában szenved: szinkron olvasás a lemezről a főszálon, az atomitás garanciáinak hiánya konkurens írásoknál, és a változások reaktív követésének lehetetlensége. A DataStore mindhármat megoldja: Flow a megfigyeléshez, fájlzárolás az atomitáshoz, és aszinkron API a szálbiztonsághoz.
DataStore az adatokat a készülék belső memóriájában lévő fájlokban tárolja. A Preferences DataStore a SharedPreferences-hez hasonló fájlformátumot használ, de további metaadatokkal az integritás ellenőrzéséhez. A Proto DataStore a Protocol Buffers bináris formátumát használja, ami csökkenti a fájlméretet és gyorsítja a szerializációt.
Adatolvasáskor a DataStore egyszerre tölti be a teljes fájlt a memóriába, majd az előfizetők Flow-n keresztül kapják meg az aktuális állapotot. A változások automatikusan továbbításra kerülnek az összes aktív előfizetőhöz — nincs szükség manuális listener regisztrációra, mint a SharedPreferences esetében.
Preferences DataStore egy Map-alapú beépített szerializációs mechanizmust használ. Minden bejegyzés egy karakterlánc és egy primitív típus (Int, Boolean, Float, Long, String, Set) párja. Az adatok egy XML fájlban tárolódnak, hasonlóan a SharedPreferences-hez, de atomi írással fájlzároláson keresztül.
Példa a Preferences DataStore létrehozására: a preferencesDataStore kiterjesztés a Context-en egy szingletont hoz létre a fájlnévvel. Ismételt hívásokkor ugyanaz a példány kerül visszaadásra — ez kiküszöböli a fájlok duplikálódását és a különböző tárolópéldányokkal való összetévesztést.
Proto DataStore megköveteli az adatséma .proto fájlon keresztüli definiálását és a protobuf plugin segítségével történő fordítását. A generált Java osztály az összes mező egyetlen belépési pontjaként szolgál — ez kiküszöböli a SharedPreferences-re jellemző elírásokat a kulcsokban.
A Proto DataStore séma egyszer kerül meghatározásra, és támogatja új mezők hozzáadását a régi adatok elvesztése nélkül. Ha az alkalmazás új verziójában egy alapértelmezett értékkel rendelkező mező kerül hozzáadásra, a régi fájl helyesen lesz deszerializálva — a visszafelé kompatibilitás be van építve a protokollba.
A választás a Preferences DataStore és a Proto DataStore között az adatok összetettségétől és a tipizálási követelményektől függ. Mindkét változat aszinkron és tranzakciós, de különböznek a type-safety szintjében és a szerializációs teljesítményben.
| Jellemző | Preferences DataStore | Proto DataStore |
|---|---|---|
| Tipizálás | Gyenge (kulcs-érték) | Erős (generált osztály) |
| Szerializáció | XML (beépített) | Protocol Buffers (protobuf) |
| Fájlméret | Nagy (olvasható XML) | Kicsi (bináris) |
| Összetettség | Alacsony (.proto nélkül) | Közepes (.proto szükséges) |
| Séma migráció | Nincs séma | Automatikus (proto) |
| Kompatibilitás | SharedPreferences (migráción keresztül) | Csak Proto DataStore |
Preferences DataStore alkalmas egyszerű beállításokhoz: funkciók bekapcsolásának jelzői, engedélyezési token karakterlánc, az alkalmazás indításainak száma. Ha kevés az adat (10–15 kulcsig) és nem igényel szigorú sémát — a Preferences DataStore minimális belépési küszöböt biztosít a protobuf plugin csatlakoztatása nélkül.
Proto DataStore akkor indokolt, ha az adatstruktúra összetett vagy változhat az alkalmazás verziói között. Például felhasználói profil beállításai vagy A/B teszt konfiguráció 20+ mezővel. A Protobuf erős tipizálást és automatikus migrációkat biztosít, ami kiküszöböli a futásidejű hibákat a kulcsok eltérése miatt.
Google beépített migrációs mechanizmust biztosít a SharedPreferencesMigration osztályon keresztül. A migráció egyszer történik meg az alkalmazás frissítése utáni első indításkor: a DataStore beolvassa az adatokat a SharedPreferences-ből, beírja a saját formátumába, és befejezettnek jelöli a migrációt.
A migráció támogatja az egyéni transzformációkat: ha a SharedPreferences-ben a kulcsok nem egyeznek a DataStore kívánt kulcsaival, egy transzformációs függvény definiálható a SharedPreferencesMigration segítségével. Ez lehetővé teszi a kulcsok átnevezését és az adattípusok megváltoztatását a migrációs folyamat során.
Első lépés: adja hozzá a DataStore-t a build.gradle-hez, és hozzon létre egy DataStore példányt migrációval: a SharedPreferencesMigration elfogadja a SharedPreferences fájl nevét és az átvinni kívánt kulcsok halmazát. Második lépés: távolítsa el az összes SharedPreferences-en keresztül működő kódot, és cserélje ki DataStore hívásokra. Harmadik — tesztelje a migrációt: az első indításkor az adatoknak meg kell jelenniük a DataStore-ban, a régi SharedPreferences fájlnak pedig meg kell szűnnie használatban lenni.
val Context.dataStore by preferencesDataStore(
name = "settings",
produceMigrations = { context ->
listOf(
SharedPreferencesMigration(context, "old_prefs")
)
}
)
DataStore könnyen integrálható egy meglévő projektbe. Az alábbiakban gyakorlati példák találhatók a Preferences DataStore és a Proto DataStore számára — mindkettő bemutatja az adatok olvasását, írását és reaktív megfigyelését.
Ebben a példában a Preferences DataStore három beállítást tárol: sötét témát, felhasználónevet és indítások számát. Az olvasás a .data kiterjesztésen keresztül történik, amely Flow-t ad vissza. Az írás — a .edit suspend függvényen keresztül, amely garantálja a változtatások atomitását.
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 megköveteli egy .proto fájl definiálását. A fordítás után létrejön a UserSettings osztály, amely az olvasáshoz és íráshoz használatos. A sémaverziók migrációi ugyanabban a .proto fájlban vannak leírva és automatikusan alkalmazásra kerülnek.
// user_preferences.proto
syntax = "proto3";
message UserPreferences {
string display_name = 1;
int32 notification_count = 2;
bool notifications_enabled = 3;
}
// Olvasás a DataStore-ból
val userPreferencesFlow: Flow<UserPreferences> =
protoDataStore.data
// Új értékek írása
suspend fun updateDisplayName(name: String) {
protoDataStore.updateData { prefs ->
prefs.toBuilder()
.setDisplayName(name)
.build()
}
}
DataStore integrálódik az MVVM architektúrával a ViewModel-en keresztül. A DataStore-ból származó Flow a .stateIn segítségével gyűjtődik össze, és az UI-ban használatos. Minden adatváltozáskor az UI automatikusan frissül — nincs szükség manuális frissítésekre vagy LiveData-ra.
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()
)
}
Gyakran ismételt kérdések
DataStore aszinkron módon működik (nem blokkolja az UI szálat), támogatja a konkurens hozzáférést tranzakciókon keresztül, és lehetővé teszi a változások reaktív követését Flow segítségével. SharedPreferences — szinkron API ANR kockázattal nagy adatmennyiségeknél és beépített reaktivitás támogatás nélkül.
DataStore Kotlinban íródott, és Kotlin Coroutines-t igényel. Java-ból való használata lehetséges, de kényelmetlen: CompletableFuture-rel burkoló objektumokat kell létrehozni vagy manuálisan kezelni a korutinokat. Java projektekhez a Google a SharedPreferences megtartását vagy a Kotlin modulhoz adását javasolja.
DataStore a teljes fájlt betölti a memóriába olvasáskor, így nem alkalmas listák vagy nagy objektumok tárolására. Ilyen esetekben használja a Room-ot vagy az SQLite-ot. A DataStore beállításokhoz és kis strukturált adatokhoz van optimalizálva — akár több száz kilobájtig.
A DataStore létrehozásakor átadható egy corruptionHandler — egy függvény, amely a fájl sérülésekor hívódik meg. Alapértelmezés szerint a DataStore CorruptionException kivételt dob. A corruptionHandler-ben üres adatok adhatók vissza, ami után a DataStore felülírja a fájlt a helyes állapottal.
Igen, a Proto DataStore megköveteli a séma .proto fájlban történő definiálását és a protobuf-gradle-plugin csatlakoztatását. Ha a projekt kicsi és az adatok egyszerűek, könnyebb a Preferences DataStore használata — nem igényel további build konfigurációt.
Összefoglalás
Kulcsrakész mobilalkalmazást fejlesztünk
Az IT Sectr 2017 óta készít iOS és Android alkalmazásokat induló vállalkozásoknak és vállalkozásoknak. Tanácsot adunk, és a legjobb megoldást javasoljuk.
Olvassa el is