SharedPreferences è un archivio dati chiave-valore su Android progettato per salvare impostazioni semplici e configurazioni dell'app. I dati vengono memorizzati in un file XML sul dispositivo e sono accessibili solo all'interno dell'app che li ha creati. Secondo la documentazione ufficiale Android Developers, 2025, SharedPreferences supporta la memorizzazione di tipi primitivi: String, Int, Boolean, Float, Long e Set<String>. Questa è la soluzione più semplice e veloce per salvare piccole quantità di impostazioni utente senza bisogno di query SQL o di lavorare direttamente con il file system.
Punti Chiave
SharedPreferences è un meccanismo integrato di Android per memorizzare coppie chiave-valore in un file XML nella memoria interna del dispositivo. È disponibile dall'API Level 1 e non richiede librerie aggiuntive. Lo scopo principale è salvare le preferenze dell'utente, lo stato dell'interfaccia, i flag di primo avvio e altri dati semplici che non richiedono un database strutturato.
Ogni file SharedPreferences è associato a un nome specifico e a una modalità di accesso. Per impostazione predefinita viene utilizzata la modalità Context.MODE_PRIVATE, che limita l'accesso al file alla sola app corrente. In precedenza Android supportava le modalità MODE_WORLD_READABLE e MODE_WORLD_WRITEABLE, ma sono state dichiarate obsolete a partire dall'API Level 17 e completamente rimosse in Android 7.0 (API 24) per motivi di sicurezza.
Nonostante la sua semplicità, SharedPreferences viene utilizzato in milioni di app Android. Secondo Google, oltre il 90% delle app pubblicate su Google Play utilizza SharedPreferences per memorizzare le impostazioni. Tuttavia, per scenari complessi (grandi volumi di dati, sicurezza dei tipi, asincronia), Google consiglia soluzioni più moderne come Preferences DataStore della libreria Android Jetpack.
Fisicamente, SharedPreferences viene memorizzato come file XML nella directory dell'app: /data/data/{package_name}/shared_prefs/{file_name}.xml. Il file contiene un elemento radice <map> con elementi figlio <string>, <int>, <boolean>, <float> e <long> a seconda del tipo di valore memorizzato. La dimensione del file non è limitata, ma per grandi volumi di dati (oltre 100 KB), le prestazioni di lettura e scrittura iniziano a degradarsi notevolmente.
I file SharedPreferences non sono crittografati per impostazione predefinita. I dati vengono memorizzati in chiaro nel file system del dispositivo. Per memorizzare dati sensibili (token, password), si consiglia di utilizzare EncryptedSharedPreferences dalla libreria AndroidX Security, che crittografa automaticamente chiavi e valori utilizzando AES256-GCM.
SharedPreferences funziona secondo il principio della memorizzazione nella cache in RAM con sincronizzazione periodica su disco. Al primo accesso al file (tramite getSharedPreferences), Android carica il file XML nella RAM e lo analizza in un oggetto Map. Tutte le successive operazioni di lettura vengono eseguite dalla memoria, senza rileggere dal disco. Ciò garantisce un'elevata velocità di accesso ai dati.
Le operazioni di scrittura utilizzano Editor — un buffer interno delle modifiche. Quando lo sviluppatore chiama putString o putBoolean, le modifiche vengono memorizzate nell'oggetto Editor in memoria. La scrittura effettiva su disco avviene quando viene chiamato il metodo commit (sincrono) o apply (asincrono). Fino a quando questi metodi non vengono chiamati, i dati non vengono salvati e, in caso di arresto anomalo dell'app, le modifiche possono essere perse.
Per ottenere un'istanza di SharedPreferences vengono utilizzati due metodi: getPreferences e getSharedPreferences. Il primo è disponibile solo all'interno di un'Activity e crea un file con il nome dell'Activity. Il secondo è più flessibile, accetta un nome file e una modalità di accesso, ed è accessibile da qualsiasi contesto (Application, Activity, Service). Si consiglia di utilizzare getSharedPreferences con un nome file corrispondente al modulo o alla funzionalità dell'app.
// Ottenere SharedPreferences
val prefs = context.getSharedPreferences(
"user_settings", Context.MODE_PRIVATE
)
// Scrivere dati
with(prefs.edit()) {
putString("username", "Anna")
putInt("age", 28)
putBoolean("isLoggedIn", true)
apply()
}
// Leggere dati
val username = prefs.getString("username", "")
val age = prefs.getInt("age", 0)
val isLoggedIn = prefs.getBoolean("isLoggedIn", false)
Quando si utilizza MODE_MULTI_PROCESS (obsoleto), SharedPreferences si sincronizza tra i processi. Tuttavia, questa sincronizzazione non garantisce l'atomicità e Google consiglia di evitare SharedPreferences in scenari multiprocesso. In tali casi, è meglio utilizzare ContentProvider, Room con accesso inter-processo o DataStore.
SharedPreferences fornisce un insieme di metodi per leggere i dati per chiave e l'interfaccia Editor per scrivere. Ogni metodo di lettura accetta due parametri: una chiave e un valore predefinito che viene restituito se la chiave non viene trovata. Il valore predefinito determina anche il tipo restituito: getString restituisce String, getInt restituisce Int e così via.
| Metodo di lettura | Metodo di scrittura | Tipo di dato |
|---|---|---|
| getString | putString | String |
| getInt | putInt | Int |
| getBoolean | putBoolean | Boolean |
| getFloat | putFloat | Float |
| getLong | putLong | Long |
| getStringSet | putStringSet | Set<String> |
Editor è un oggetto interno di SharedPreferences che raccoglie le modifiche in un buffer. Dopo aver apportato tutte le modifiche, lo sviluppatore chiama commit() (scrittura sincrona) o apply() (scrittura asincrona). La differenza è critica: commit blocca il thread corrente fino al completamento della scrittura su disco e restituisce un boolean (successo/fallimento), mentre apply esegue la scrittura in un thread in background e restituisce immediatamente il controllo, ma non restituisce un risultato.
Si consiglia di utilizzare apply invece di commit in tutti i casi in cui non è necessario conoscere il risultato della scrittura. apply è più veloce e non blocca il thread dell'interfaccia utente. commit dovrebbe essere utilizzato solo quando è fondamentale sapere se i dati sono stati salvati correttamente, o quando si lavora in modalità multiprocesso. Per rimuovere singole chiavi viene utilizzato il metodo remove, per la cancellazione completa — clear. Tutte le operazioni di eliminazione vengono eseguite anche tramite Editor.
// Modifiche multiple - un apply
prefs.edit {
putString("theme", "dark")
putBoolean("notifications", false)
remove("old_key")
}
// Listener di cambio valore
prefs.registerOnSharedPreferenceChangeListener { prefs, key ->
Log.d("TAG", "Chiave modificata: $key")
}
A partire da Android 12 (API 31), SharedPreferences è stato migliorato con il supporto di registerOnSharedPreferenceChangeListener con disiscrizione automatica tramite Lifecycle. Ciò aiuta a evitare perdite di memoria associate a listener dimenticati. Nelle versioni precedenti, lo sviluppatore deve chiamare manualmente unregisterOnSharedPreferenceChangeListener in onDestroy o onStop del componente.
Nonostante il suo uso diffuso, SharedPreferences non è una soluzione universale per tutti gli scenari di archiviazione dati su Android. A seconda del volume di dati, dei requisiti di sicurezza dei tipi e delle prestazioni, Google consiglia varie alternative incluse in Android Jetpack e nella libreria standard di Android.
| Soluzione | Quando usarla | Svantaggi |
|---|---|---|
| SharedPreferences | Impostazioni piccole (fino a 100 chiavi) | Nessuna sicurezza dei tipi, lettura sincrona |
| DataStore | Impostazioni di complessità media con coroutine | Nessuna compatibilità all'indietro sotto API 14 |
| Room | Dati strutturati e liste | Eccessivo per 3-5 impostazioni |
| EncryptedSharedPreferences | Dati sensibili e token | Dipende da AndroidX Security |
DataStore è una libreria Android Jetpack presentata da Google come sostituto di SharedPreferences. Fornisce due varianti: Preferences DataStore (chiave-valore, come SharedPreferences) e Proto DataStore (archiviazione tipizzata tramite Protocol Buffers). DataStore utilizza coroutine e Flow per il funzionamento asincrono, garantisce la sicurezza dei tipi e gestisce automaticamente le migrazioni di versione. Google consiglia DataStore per tutti i nuovi progetti.
Il vantaggio principale di DataStore è l'asincronia a livello API. Tutte le operazioni di lettura restituiscono Flow e le operazioni di scrittura sono funzioni suspend. Ciò elimina completamente il blocco del thread dell'interfaccia utente che può verificarsi con la lettura sincrona di SharedPreferences. Inoltre, DataStore garantisce la coerenza dei dati: la scrittura viene eseguita in una transazione e, in caso di errore, tutte le modifiche vengono annullate.
Consideriamo un esempio pratico: impostazioni del tema (chiaro/scuro/sistema) in un'app Android. L'utente seleziona un tema e la scelta viene salvata in SharedPreferences. Ai successivi avvii dell'app, il tema viene ripristinato dalle impostazioni salvate. Per aggiornamenti reattivi dell'interfaccia, viene utilizzata l'osservazione delle modifiche tramite SharedPreferences.OnSharedPreferenceChangeListener.
Creiamo una classe ThemePreferences che incapsula tutto il lavoro con SharedPreferences per il tema. La classe fornisce i metodi getTheme (lettura), setTheme (scrittura) e observeTheme (osservazione). Il nome del file delle impostazioni sarà “app_preferences” con modalità MODE_PRIVATE. Per comodità, le chiavi sono inserite in un oggetto companion come costanti.
class ThemePreferences(context: Context) {
companion object {
private const val PREF_NAME = "app_preferences"
private const val KEY_THEME = "theme_mode"
const val THEME_LIGHT = "light"
const val THEME_DARK = "dark"
const val THEME_SYSTEM = "system"
}
private val prefs = context
.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
fun getTheme(): String =
prefs.getString(KEY_THEME, THEME_SYSTEM) ?: THEME_SYSTEM
fun setTheme(theme: String) {
prefs.edit { putString(KEY_THEME, theme) }
}
fun observeTheme(callback: (String) -> Unit) {
prefs.registerOnSharedPreferenceChangeListener { _, key ->
if (key == KEY_THEME) {
callback.invoke(getTheme())
}
}
}
}
In un'Activity o Fragment, l'ottenimento di un'istanza di ThemePreferences viene effettuato tramite il contesto dell'app. All'inizializzazione, viene chiamato getTheme per impostare il tema corrente. Quando l'utente seleziona un nuovo tema, viene chiamato setTheme e tramite observeTheme l'interfaccia viene aggiornata senza riavviare l'Activity. È importante disiscriversi dal listener in onDestroy per evitare perdite di memoria, specialmente se l'Activity viene ricreata al cambiamento della configurazione.
Per le app con versione minima target Android 12+, si consiglia di utilizzare registerOnSharedPreferenceChangeListener insieme a LifecycleObserver. Ciò gestisce automaticamente l'iscrizione e la disiscrizione al cambiamento del ciclo di vita del componente. Per le versioni precedenti, l'iscrizione e la disiscrizione devono essere gestite manualmente, il che è una frequente fonte di errori nelle app di produzione che utilizzano SharedPreferences.
Domande Frequenti
SharedPreferences supporta direttamente solo tipi primitivi e Set<String>. Per memorizzare oggetti, è necessario serializzarli in una stringa JSON tramite Gson o Moshi, salvarli tramite putString e deserializzarli durante la lettura. Per oggetti complessi con molti campi, si consiglia di utilizzare Room invece di SharedPreferences con serializzazione JSON.
Sì, SharedPreferences è thread-safe. Tutte le operazioni di lettura e scrittura sono sincronizzate a livello dell'oggetto SharedPreferences e del suo Editor. Tuttavia, quando si utilizza la modalità multiprocesso, la sincronizzazione non è garantita. Per l'accesso concorrente da più thread all'interno di una singola app, SharedPreferences è sicuro senza blocchi aggiuntivi.
Per cancellare completamente tutti i dati da SharedPreferences, chiamare il metodo clear() su Editor e applicare le modifiche tramite apply. Se è necessario eliminare il file XML stesso, utilizzare deleteSharedPreferences(name) sul contesto. La cancellazione dei dati dell'app tramite Impostazioni → App → Cancella dati rimuove anche tutti i file SharedPreferences.
Per i nuovi progetti, Google consiglia DataStore come sostituto di SharedPreferences. DataStore offre funzionamento asincrono con coroutine, sicurezza dei tipi (Proto DataStore) e migrazioni automatiche. SharedPreferences dovrebbe essere scelto solo per progetti con versione minima inferiore all'API 14 o quando è necessaria un'integrazione rapida senza dipendenze aggiuntive.
Per la crittografia dei dati, utilizzare EncryptedSharedPreferences dalla libreria AndroidX Security. Crittografa automaticamente chiavi e valori utilizzando AES-256 GCM. Il processo di configurazione è minimo: getSharedPreferences viene sostituito con EncryptedSharedPreferences.create specificando una chiave master da Android Keystore.
Riepilogo
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.
Leggi anche