DataStore — en komponent från Jetpack-biblioteket avsedd för lagring av små mängder data i Android-applikationer. Till skillnad från SharedPreferences fungerar den asynkront och garanterar datakonsistens vid samtidig åtkomst. Enligt Google, 2024 använder DataStore Kotlin Coroutines och Flow, vilket gör det säkert för huvudtråden och lämpligt för reaktiva arkitekturer.
Huvudpunkter
DataStore — en lösning från Google för lokal datalagring i Android, presenterad 2020 som ett alternativ till SharedPreferences. Den stöder två lägen: Preferences DataStore (enkla nyckel-värdepar) och Proto DataStore (typat schema baserat på Protocol Buffers).
Den främsta fördelen — fullständig asynkronicitet: alla läsoperationer returnerar Flow från Kotlin Coroutines och skrivning utförs i coroutine-kontext. Detta eliminerar blockering av huvudtråden, vilket var ett typiskt problem med SharedPreferences vid arbete med stora datavolymer.
DataStore garanterar atomicitet av operationer: samtidiga skrivningar leder inte till dataförlust tack vare transaktionsmodellen. Om två komponenter samtidigt ändrar samma värde, hanterar DataStore konflikten korrekt via compare-and-swap-mekanismen.
Enligt Google I/O 2023 används DataStore i 40% av nya projekt på Android, och Google rekommenderar migrering från SharedPreferences i alla applikationer där stabilitet i inställningslagring krävs.
I grunden av DataStore ligger SingleProcessDataStore — en implementering som fungerar inom en enda process. Den använder fillagring med lås på filnivå: vid dataskrivning låses filen, vilket förhindrar skada vid samtidig åtkomst.
DataStore hanterar automatiskt deserialiseringsfel: om filen är skadad returneras standardvärdet och filen skrivs över. Detta beteende konfigureras via corruptionHandler, som kan ställas in vid skapandet av DataStore.
SharedPreferences lider av tre grundläggande problem: synkron läsning från disk på huvudtråden, brist på atomicitetsgarantier vid samtidiga skrivningar och omöjlighet att reaktivt spåra ändringar. DataStore löser alla tre: Flow för observation, fillås för atomicitet och asynkront API för trådsäkerhet.
DataStore lagrar data i filer på enhetens interna minne. Preferences DataStore använder ett filformat som liknar SharedPreferences, men med extra metadata för integritetskontroll. Proto DataStore använder binärformatet Protocol Buffers, vilket minskar filstorleken och påskyndar serialisering.
Vid dataläsning laddar DataStore hela filen i minnet en gång, varefter prenumeranter får aktuell status via Flow. Ändringar överförs automatiskt till alla aktiva prenumeranter — manuell registrering av listeners, som i SharedPreferences, krävs inte.
Preferences DataStore använder en inbyggd serialiseringsmekanism baserad på Map. Varje post är ett par av en sträng och en primitiv typ (Int, Boolean, Float, Long, String, Set). Data lagras i en XML-fil, liknande SharedPreferences, men med atomär skrivning via fillås.
Exempel på att skapa Preferences DataStore: tillägget preferencesDataStore på Context skapar en singleton med filnamnet. Vid upprepade anrop returneras samma instans — detta eliminerar duplicering av filer och förvirring med olika lagringsinstanser.
Proto DataStore kräver att dataschemat definieras via en .proto-fil och kompilering med protobuf-plugin. Den genererade Java-klassen används som enda inmatningspunkt för alla fält — detta eliminerar skrivfel i nycklar, typiskt för SharedPreferences.
Proto DataStore-schemat definieras en gång och stöder tillägg av nya fält utan förlust av gamla data. Om ett fält med standardvärde läggs till i en ny version av applikationen, kommer den gamla filen att deserialiseras korrekt — bakåtkompatibilitet är inbyggd i protokollet.
Valet mellan Preferences DataStore och Proto DataStore beror på datakomplexiteten och typkraven. Båda varianterna är asynkrona och transaktionsbaserade, men skiljer sig i nivån av type-safety och serialiseringsprestanda.
| Egenskap | Preferences DataStore | Proto DataStore |
|---|---|---|
| Typning | Svag (nyckel-värde) | Stark (genererad klass) |
| Serialisering | XML (inbyggd) | Protocol Buffers (protobuf) |
| Filstorlek | Stor (läsbar XML) | Liten (binär) |
| Komplexitet | Låg (utan .proto) | Medel (kräver .proto) |
| Schemamigrering | Inget schema | Automatisk (proto) |
| Kompatibilitet | SharedPreferences (via migrering) | Endast Proto DataStore |
Preferences DataStore passar för enkla inställningar: funktionsaktiveringsflaggor, auktoriseringstokensträng, antal applikationsstarter. Om data är lite (upp till 10–15 nycklar) och inte kräver strikt schema — ger Preferences DataStore en minimal inträdesnivå utan att ansluta protobuf-plugin.
Proto DataStore är motiverat när datastrukturen är komplex eller kan ändras mellan applikationsversioner. Till exempel användarprofilinställningar eller A/B-testkonfiguration med 20+ fält. Protobuf ger stark typning och automatiska migreringar, vilket eliminerar körningsfel på grund av nyckelmissmatchning.
Google tillhandahåller en inbyggd migreringsmekanism via klassen SharedPreferencesMigration. Migreringen utförs en gång vid första starten efter applikationsuppdatering: DataStore läser data från SharedPreferences, skriver dem i sitt eget format och markerar migreringen som slutförd.
Migrering stöder anpassade transformationer: om nycklarna i SharedPreferences inte matchar de önskade DataStore-nycklarna, kan en transformationsfunktion definieras via SharedPreferencesMigration. Detta möjliggör omdöpning av nycklar och ändring av datatyper under migreringsprocessen.
Första steget: lägg till DataStore i build.gradle och skapa en DataStore-instans med migrering: SharedPreferencesMigration accepterar namnet på SharedPreferences-filen och uppsättningen nycklar som ska överföras. Andra steget: ta bort all kod som fungerar via SharedPreferences och ersätt den med DataStore-anrop. Tredje — testa migreringen: vid första starten ska data visas i DataStore och den gamla SharedPreferences-filen ska sluta användas.
val Context.dataStore by preferencesDataStore(
name = "settings",
produceMigrations = { context ->
listOf(
SharedPreferencesMigration(context, "old_prefs")
)
}
)
DataStore integreras enkelt i ett befintligt projekt. Nedan ges praktiska exempel för Preferences DataStore och Proto DataStore — båda demonstrerar läsning, skrivning och reaktiv observation av data.
I detta exempel lagrar Preferences DataStore tre inställningar: mörkt tema, användarnamn och antal starter. Läsning sker via tillägget .data, som returnerar Flow. Skrivning — via suspend-funktionen .edit, som garanterar atomicitet av ändringar.
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 kräver definition av en .proto-fil. Efter kompilering skapas klassen UserSettings, som används för läsning och skrivning. Schemaversioners migreringar beskrivs i samma .proto-fil och tillämpas automatiskt.
// user_preferences.proto
syntax = "proto3";
message UserPreferences {
string display_name = 1;
int32 notification_count = 2;
bool notifications_enabled = 3;
}
// Läsning från DataStore
val userPreferencesFlow: Flow<UserPreferences> =
protoDataStore.data
// Skrivning av nya värden
suspend fun updateDisplayName(name: String) {
protoDataStore.updateData { prefs ->
prefs.toBuilder()
.setDisplayName(name)
.build()
}
}
DataStore integreras med MVVM-arkitektur via ViewModel. Flow från DataStore samlas via .stateIn och används i UI. Vid varje dataändring uppdateras UI automatiskt — manuella uppdateringar eller LiveData behövs inte.
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()
)
}
Vanliga frågor
DataStore fungerar asynkront (blockerar inte UI-tråden), stöder samtidig åtkomst via transaktioner och möjliggör reaktiv prenumeration på ändringar via Flow. SharedPreferences — synkront API med ANR-risk vid stora datavolymer och utan inbyggt stöd för reaktivitet.
DataStore är skrivet i Kotlin och kräver Kotlin Coroutines. Att använda det från Java är möjligt men obekvämt: man måste skapa omslag med CompletableFuture eller manuellt hantera coroutines. För Java-projekt rekommenderar Google att behålla SharedPreferences eller lägga till Kotlin i modulen.
DataStore laddar hela filen i minnet vid läsning, så det är inte lämpligt för lagring av listor eller stora objekt. För sådana scenarier, använd Room eller SQLite. DataStore är optimerat för inställningar och små strukturerade data — upp till hundratals kilobyte.
Vid skapandet av DataStore kan corruptionHandler skickas — en funktion som anropas vid filskada. Som standard kastar DataStore ett CorruptionException. I corruptionHandler kan tom data returneras, varefter DataStore skriver över filen med korrekt tillstånd.
Ja, Proto DataStore kräver att schemat definieras i en .proto-fil och att protobuf-gradle-plugin ansluts. Om projektet är litet och data enkel, är det enklare att använda Preferences DataStore — det kräver ingen extra byggkonfiguration.
Sammanfattning
Vi utvecklar en mobil applikation nyckelfärdigt
IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.
Läs också