DataStore: cos'è, basi dell'archiviazione dati e sostituzione di SharedPreferences

Autore: IT Sectr Pubblicato: 2026-03-12 Tempo di lettura: 9 min

DataStore è un componente della libreria Jetpack progettato per archiviare piccole quantità di dati nelle applicazioni Android. A differenza di SharedPreferences, funziona in modo asincrono e garantisce la coerenza dei dati in caso di accesso concorrente. Secondo Google, 2024, DataStore utilizza Kotlin Coroutines e Flow, rendendolo sicuro per il thread principale e adatto per architetture reattive.

Punti Chiave

  • DataStore — un sostituto di SharedPreferences con API asincrona e tipi di dati tramite Protocol Buffers
  • Preferences DataStore — un semplice archivio chiave-valore con lettura tramite Flow e transazioni
  • Proto DataStore — un archivio tipizzato con migrazioni automatiche dello schema
  • SharedPreferences — API sincrona che blocca il thread UI con grandi volumi
  • Migrazione eseguita tramite l'interfaccia SharedPreferencesMigration senza perdita di dati

Cos'è DataStore?

DataStore è una soluzione di Google per l'archiviazione locale dei dati in Android, presentata nel 2020 come alternativa a SharedPreferences. Supporta due modalità: Preferences DataStore (semplici coppie chiave-valore) e Proto DataStore (schema tipizzato basato su Protocol Buffers).

Il vantaggio principale è la completa asincronia: tutte le operazioni di lettura restituiscono un Flow da Kotlin Coroutines e le scritture vengono eseguite in un contesto di coroutine. Ciò elimina il blocco del thread principale, che era un problema tipico di SharedPreferences nella gestione di grandi volumi di dati.

DataStore garantisce l'atomicità delle operazioni: le scritture concorrenti non causano perdita di dati grazie al suo modello transazionale. Se due componenti modificano lo stesso valore simultaneamente, DataStore gestisce correttamente il conflitto tramite un meccanismo compare-and-swap.

Secondo Google I/O 2023, DataStore è utilizzato nel 40% dei nuovi progetti Android e Google raccomanda di migrare da SharedPreferences in tutte le applicazioni dove è richiesta stabilità nell'archiviazione delle impostazioni.

Architettura di DataStore

Al centro di DataStore c'è SingleProcessDataStore — un'implementazione che opera all'interno di un singolo processo. Utilizza un archivio basato su file con blocco a livello di file: durante la scrittura dei dati, il file viene bloccato, prevenendo la corruzione in caso di accesso concorrente.

DataStore gestisce automaticamente gli errori di deserializzazione: se il file è danneggiato, restituisce un valore predefinito e sovrascrive il file. Questo comportamento è configurabile tramite corruptionHandler, che può essere impostato durante la creazione di DataStore.

Problemi di SharedPreferences che DataStore risolve

SharedPreferences soffre di tre problemi fondamentali: lettura sincrona dal disco sul thread principale, mancanza di garanzie di atomicità per scritture concorrenti e incapacità di tracciare i cambiamenti in modo reattivo. DataStore risolve tutti e tre: Flow per l'osservazione, blocco dei file per l'atomicità e API asincrona per la sicurezza dei thread.

Come funziona DataStore in Android?

DataStore memorizza i dati in file nella memoria interna del dispositivo. Preferences DataStore utilizza un formato di file simile a SharedPreferences ma con metadati aggiuntivi per la verifica dell'integrità. Proto DataStore utilizza il formato binario Protocol Buffers, che riduce le dimensioni del file e accelera la serializzazione.

Durante la lettura dei dati, DataStore carica l'intero file in memoria una volta, dopo di che gli abbonati ricevono lo stato corrente tramite Flow. Le modifiche vengono trasmesse automaticamente a tutti gli abbonati attivi — non è richiesta la registrazione manuale di listener, come in SharedPreferences.

Come funziona Preferences DataStore

Preferences DataStore utilizza un meccanismo di serializzazione integrato basato su una Map. Ogni voce è una coppia di una stringa e un tipo primitivo (Int, Boolean, Float, Long, String, Set). I dati sono memorizzati in un file XML, simile a SharedPreferences, ma con scrittura atomica tramite blocco del file.

Esempio di creazione di Preferences DataStore: l'estensione preferencesDataStore su Context crea un singleton con il nome del file. A chiamate ripetute, viene restituita la stessa istanza — ciò elimina la duplicazione dei file e la confusione tra diverse istanze di archiviazione.

Come funziona Proto DataStore

Proto DataStore richiede la definizione di uno schema dati tramite un file .proto e la compilazione con il plugin protobuf. La classe Java generata viene utilizzata come punto di ingresso unico per tutti i campi — ciò elimina gli errori di battitura nelle chiavi, comuni con SharedPreferences.

Lo schema di Proto DataStore viene definito una volta e supporta l'aggiunta di nuovi campi senza perdere i vecchi dati. Se una nuova versione dell'app aggiunge un campo con un valore predefinito, il vecchio file verrà deserializzato correttamente — la compatibilità all'indietro è integrata nel protocollo.

Preferences DataStore vs Proto DataStore: Confronto

La scelta tra Preferences DataStore e Proto DataStore dipende dalla complessità dei dati e dai requisiti di tipizzazione. Entrambe le opzioni sono asincrone e transazionali, ma differiscono per sicurezza dei tipi e prestazioni di serializzazione.

CaratteristicaPreferences DataStoreProto DataStore
TipizzazioneDebole (chiave-valore)Forte (classe generata)
SerializzazioneXML (integrata)Protocol Buffers (protobuf)
Dimensione fileGrande (XML leggibile)Piccola (binaria)
ComplessitàBassa (senza .proto)Media (richiede .proto)
Migrazione schemaNessuno schemaAutomatica (proto)
CompatibilitàSharedPreferences (tramite migrazione)Solo Proto DataStore

Quando scegliere Preferences DataStore

Preferences DataStore è adatto per impostazioni semplici: flag di funzionalità, stringa del token di autorizzazione, conteggio avvii dell'app. Se i dati sono pochi (fino a 10–15 chiavi) e non richiedono uno schema rigoroso, Preferences DataStore fornisce una soglia di ingresso minima senza collegare il plugin protobuf.

Quando scegliere Proto DataStore

Proto DataStore è giustificato quando la struttura dei dati è complessa o può cambiare tra le versioni dell'app. Ad esempio, impostazioni del profilo utente o configurazione di test A/B con 20+ campi. Protobuf fornisce una tipizzazione forte e migrazioni automatiche, eliminando errori di runtime dovuti a discrepanze di chiavi.

Come migrare da SharedPreferences a DataStore

Google fornisce un meccanismo di migrazione integrato tramite la classe SharedPreferencesMigration. La migrazione viene eseguita una volta al primo avvio dopo l'aggiornamento dell'app: DataStore legge i dati da SharedPreferences, li scrive nel proprio formato e contrassegna la migrazione come completata.

La migrazione supporta trasformazioni personalizzate: se le chiavi in SharedPreferences non corrispondono alle chiavi desiderate di DataStore, è possibile specificare una funzione di trasformazione tramite SharedPreferencesMigration. Ciò consente di rinominare le chiavi e cambiare i tipi di dati durante la migrazione.

Migrazione passo passo

Primo, aggiungi DataStore a build.gradle e crea un'istanza di DataStore con migrazione: SharedPreferencesMigration accetta il nome del file SharedPreferences e un insieme di chiavi da trasferire. Secondo, rimuovi tutto il codice che funziona tramite SharedPreferences e sostituiscilo con chiamate a DataStore. Terzo, testa la migrazione: al primo avvio, i dati dovrebbero apparire in DataStore e il vecchio file SharedPreferences non dovrebbe più essere utilizzato.

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

Esempi di utilizzo di DataStore nel codice

DataStore si integra facilmente in un progetto esistente. Di seguito sono riportati esempi pratici per Preferences DataStore e Proto DataStore — entrambi dimostrano la lettura, la scrittura e l'osservazione reattiva dei dati.

Preferences DataStore: Lettura e scrittura delle impostazioni

In questo esempio, Preferences DataStore memorizza tre impostazioni: tema scuro, nome utente e conteggio avvii. La lettura avviene tramite l'estensione .data, che restituisce un Flow. La scrittura avviene tramite la funzione suspend .edit, che garantisce l'atomicità delle modifiche.

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: Schema e utilizzo

Proto DataStore richiede la definizione di un file .proto. Dopo la compilazione, viene creata una classe UserSettings utilizzata per lettura e scrittura. Le migrazioni di versione dello schema sono descritte nello stesso file .proto e vengono applicate automaticamente.

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

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

// Lettura da DataStore
val userPreferencesFlow: Flow<UserPreferences> =
    protoDataStore.data

// Scrittura di nuovi valori
suspend fun updateDisplayName(name: String) {
    protoDataStore.updateData { prefs ->
        prefs.toBuilder()
            .setDisplayName(name)
            .build()
    }
}

Osservazione reattiva delle modifiche

DataStore si integra con l'architettura MVVM tramite ViewModel. Il Flow da DataStore viene raccolto tramite .stateIn e utilizzato nell'interfaccia utente. A ogni modifica dei dati, l'interfaccia utente si aggiorna automaticamente — non sono necessari aggiornamenti manuali o 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()
            )
}

Domande Frequenti

In cosa DataStore è migliore di SharedPreferences?

DataStore funziona in modo asincrono (non blocca il thread UI), supporta l'accesso concorrente tramite transazioni e consente l'abbonamento reattivo alle modifiche tramite Flow. SharedPreferences è un'API sincrona con rischio di ANR con grandi volumi di dati e senza supporto reattivo integrato.

Si può usare DataStore con Java?

DataStore è scritto in Kotlin e richiede Kotlin Coroutines. Usarlo da Java è possibile ma scomodo: si dovrebbero creare wrapper con CompletableFuture o gestire manualmente le coroutine. Per progetti Java, Google raccomanda di mantenere SharedPreferences o aggiungere Kotlin al modulo.

DataStore è adatto per archiviare grandi volumi di dati?

DataStore carica l'intero file in memoria durante la lettura, quindi non è adatto per archiviare elenchi o oggetti grandi. Per tali scenari, usa Room o SQLite. DataStore è ottimizzato per impostazioni e piccoli dati strutturati — fino a centinaia di kilobyte.

Come gestire un errore di file DataStore danneggiato?

Durante la creazione di DataStore, puoi passare un corruptionHandler — una funzione chiamata quando il file è danneggiato. Per impostazione predefinita, DataStore lancia un'eccezione CorruptionException. Nel corruptionHandler, puoi restituire dati vuoti, dopodiché DataStore sovrascriverà il file con uno stato corretto.

Proto DataStore richiede un file .proto?

Sì, Proto DataStore richiede la definizione di uno schema in un file .proto e la connessione del protobuf-gradle-plugin. Se il progetto è piccolo e i dati sono semplici, è più facile usare Preferences DataStore — non richiede configurazione di build aggiuntiva.

Riepilogo

  • DataStore — un sostituto moderno di SharedPreferences, che funziona con Kotlin Coroutines e Flow
  • Preferences DataStore — semplice chiave-valore senza schema, adatto per impostazioni
  • Proto DataStore — archivio tipizzato con schema protobuf e auto-migrazione
  • Migrazione da SharedPreferences è integrata in DataStore tramite SharedPreferencesMigration
  • Sicurezza dei thread — tutte le operazioni sono asincrone, il blocco dell'UI è eliminato
  • Reattività — Flow notifica gli abbonati a ogni modifica dei dati
  • Raccomandazione — usa DataStore in tutti i nuovi progetti Android, migra quelli esistenti quando lavori con le impostazioni

Svilupperemo un'applicazione mobile chiavi in mano

IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.

Discuti il progetto

Leggi anche