Working with SharedPreferences
1. Getting SharedPreferences Instance
| Method | Description | Return Type | Notes |
|---|---|---|---|
| getSharedPreferences(String name, int mode) | Retrieves a SharedPreferences instance for a specific shared preferences file | SharedPreferences |
Creates the file if it does not exist |
| getSharedPreferences(int resId, int mode) | Retrieves a SharedPreferences instance using a resource ID | SharedPreferences |
Useful for centralized preference management |
Example: Getting SharedPreferences Instance
val prefs = context.getSharedPreferences("user_prefs", Context.MODE_PRIVATE)
val prefsFromRes = context.getSharedPreferences(R.xml.preferences, Context.MODE_PRIVATE)
Note: The
mode parameter controls file access permissions. Common values are Context.MODE_PRIVATE, Context.MODE_WORLD_READABLE, and Context.MODE_WORLD_WRITEABLE.2. Storing Primitives
| Primitive Type | Method | Description |
|---|---|---|
| Boolean | edit().putBoolean(key, value) |
Stores a boolean value |
| Integer | edit().putInt(key, value) |
Stores an integer value |
| Long | edit().putLong(key, value) |
Stores a long value |
| Float | edit().putFloat(key, value) |
Stores a float value |
| String | edit().putString(key, value) |
Stores a string value |
Example: Storing and Retrieving Boolean
val prefs = getSharedPreferences("MyPrefs", Context.MODE_PRIVATE)
val editor = prefs.edit()
editor.putBoolean("isDarkMode", true)
editor.apply()
val isDarkMode = prefs.getBoolean("isDarkMode", false)
Example: Storing and Retrieving Integer
val prefs = getSharedPreferences("MyPrefs", Context.MODE_PRIVATE)
val editor = prefs.edit()
editor.putInt("userAge", 25)
editor.apply()
val userAge = prefs.getInt("userAge", 0)
3. Retrieving Values
| Method | Description | Return Type | Notes |
|---|---|---|---|
| getString(key, defaultValue) | Retrieves a String value from SharedPreferences | String? | Use null as default if no value exists |
| getInt(key, defaultValue) | Retrieves an Int value from SharedPreferences | Int | Default value is returned if key does not exist |
| getBoolean(key, defaultValue) | Retrieves a Boolean value from SharedPreferences | Boolean | Default value is returned if key does not exist |
| getFloat(key, defaultValue) | Retrieves a Float value from SharedPreferences | Float | Default value is returned if key does not exist |
| getLong(key, defaultValue) | Retrieves a Long value from SharedPreferences | Long | Default value is returned if key does not exist |
| getStringSet(key, defaultValue) | Retrieves a Set<String> value from SharedPreferences | Set<String>? | Use null as default if no value exists |
Example: Retrieving Values
val prefs = context.getSharedPreferences("MyPrefs", Context.MODE_PRIVATE)
val username = prefs.getString("username", "defaultUser")
val userAge = prefs.getInt("age", 0)
val isPremium = prefs.getBoolean("isPremium", false)
val theme = prefs.getStringSet("themes", setOf("light"))
Note: If a key does not exist, the default value is returned. Always ensure that your default values make sense for the expected data type.
4. Using Preferences Editor
Example: Saving and Editing Preferences
val prefs = getSharedPreferences("MyPrefs", Context.MODE_PRIVATE)
val editor = prefs.edit()
editor.putString("username", "john_doe")
editor.putInt("user_id", 12345)
editor.putBoolean("is_logged_in", true)
editor.apply()
| Method | Description | Notes |
|---|---|---|
| putString(key, value) | Stores a string value | Key must be unique |
| putInt(key, value) | Stores an integer value | |
| putBoolean(key, value) | Stores a boolean value | |
| putFloat(key, value) | Stores a float value | |
| putLong(key, value) | Stores a long value | |
| putStringSet(key, value) | Stores a set of strings | Available from API 11 |
| remove(key) | Removes a key-value pair | |
| clear() | Removes all key-value pairs | |
| apply() | Asynchronously saves changes | Recommended over commit() |
| commit() | Synchronously saves changes | Returns boolean success status |
Note:
apply() is preferred over commit() for better performance as it runs asynchronously.5. Removing Data
SharedPreferences allows you to remove specific keys or clear all data using the edit() method.
| Action | Description | Code Example |
|---|---|---|
| remove(key) | Removes the key-value pair associated with the specified key. | |
| clear() | Removes all key-value pairs from the SharedPreferences. | |
Note: Changes made using
edit() must be committed with either apply() or commit(). apply() is asynchronous and preferred for performance.6. Implementing OnSharedPreferenceChangeListener
Use OnSharedPreferenceChangeListener to observe changes to SharedPreferences values in real time.
Example: Registering a Listener
val prefs = getSharedPreferences("MyPrefs", Context.MODE_PRIVATE)
val listener = SharedPreferences.OnSharedPreferenceChangeListener { sharedPreferences, key ->
when (key) {
"theme" -> {
val theme = sharedPreferences.getString("theme", "light")
// Update UI based on theme change
}
"notifications" -> {
val enabled = sharedPreferences.getBoolean("notifications", true)
// Toggle notification behavior
}
}
}
prefs.registerOnSharedPreferenceChangeListener(listener)
| Method | Description | Notes |
|---|---|---|
| registerOnSharedPreferenceChangeListener() | Registers a listener for preference changes | Must be called on the main thread |
| unregisterOnSharedPreferenceChangeListener() | Unregisters a previously registered listener | Prevents memory leaks if not unregistered |
Note: The listener is called on the main thread, so avoid heavy operations inside it.
Warning: If the listener is not unregistered, it can cause memory leaks or unexpected behavior.
7. Using Encrypted SharedPreferences
| Feature | Description | Notes |
|---|---|---|
| EncryptedSharedPreferences | EncryptedSharedPreferences provides secure storage for key-value pairs using AES-256 encryption. |
Available in Android Security Library |
| Master Key | Uses a master key derived from Android Keystore for encryption. | Automatically managed by the library |
Example: Creating EncryptedSharedPreferences
import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKeys
val masterKeyAlias = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC)
val sharedPreferences = EncryptedSharedPreferences.create(
"encrypted_preferences",
masterKeyAlias,
context,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_GCM,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
Example: Storing and Retrieving Data
val editor = sharedPreferences.edit()
editor.putString("username", "user123")
editor.apply()
val username = sharedPreferences.getString("username", "")
Note:
EncryptedSharedPreferences is not suitable for storing large amounts of data due to encryption overhead.
8. Applying Best Practices
Best Practices for SharedPreferences
- Use
apply()for asynchronous saving to avoid blocking the main thread - Use
commit()only when synchronous behavior is required (e.g., before app exit) - Store only primitive data types and small amounts of data (avoid large objects or JSON)
- Use
SharedPreferencesfor simple key-value storage, not complex data structures - Prefer
Context.MODE_PRIVATEto restrict access to the app only - Use
Editorwithapply()in a single transaction to improve performance - Avoid storing sensitive data (e.g., passwords) in
SharedPreferences - Use
apply()withrunBlockingor similar for coroutine-based async operations
| Method | Description | Usage | Notes |
|---|---|---|---|
| apply() | Asynchronously saves changes to disk | editor.apply() |
Non-blocking, preferred for most use cases |
| commit() | Synchronously saves changes to disk | editor.commit() |
Blocking, returns boolean indicating success |
| getString() | Retrieves a string value | prefs.getString("key", "default") |
Use default value for safety |
| getInt() | Retrieves an integer value | prefs.getInt("key", 0) |
Use default value for safety |
| getBoolean() | Retrieves a boolean value | prefs.getBoolean("key", false) |
Use default value for safety |
Example: Saving and Retrieving Data
val prefs = context.getSharedPreferences("MyPrefs", Context.MODE_PRIVATE)
val editor = prefs.edit()
// Saving data
editor.putString("username", "john_doe")
editor.putBoolean("is_logged_in", true)
editor.apply()
// Retrieving data
val username = prefs.getString("username", "")
val isLoggedIn = prefs.getBoolean("is_logged_in", false)
Warning: SharedPreferences is not suitable for storing large or sensitive data. Use
EncryptedSharedPreferences for sensitive values.
Note: SharedPreferences is not thread-safe. If multiple threads access it, use
synchronized or Handler to prevent race conditions.
9. Storing Collections
Note: SharedPreferences does not natively support storing collections like
ArrayList or Set. You must serialize them to a string format before storing.| Collection Type | Storage Method | Code Example | Notes |
|---|---|---|---|
| List<String> | Convert to Set<String> |
|
Use putStringSet for lists of strings |
| List<Int> | Serialize to JSON or comma-separated string | |
Requires external library like Gson or Moshi |
| Set<String> | Use putStringSet |
|
Native support in SharedPreferences |
Example: Storing and Retrieving a List of Integers
import com.google.gson.Gson
val gson = Gson()
val intList = listOf(10, 20, 30)
// Store
val json = gson.toJson(intList)
sharedPreferences.edit().putString("integers", json).apply()
// Retrieve
val retrievedJson = sharedPreferences.getString("integers", null)
val retrievedList = if (retrievedJson != null) gson.fromJson(retrievedJson, List::class.java) else emptyList()
Note: When storing collections, always consider the size limits of SharedPreferences. Large collections may cause performance issues or exceed storage limits.
10. Migrating to DataStore
SharedPreferences vs DataStore
- SharedPreferences: Legacy, synchronous, stored in XML files
- DataStore: Modern, asynchronous, stored in SQLite or ProtoBuf
| Feature | SharedPreferences | DataStore |
|---|---|---|
| Storage Format | XML |
SQLite or ProtoBuf |
| Read/Write Mode | Synchronous | Asynchronous |
| Type Safety | Not type-safe | Type-safe |
| Thread Safety | Not thread-safe | Thread-safe |
| Data Observability | No built-in observability | Supports Flow for observing changes |
Example: Migrating from SharedPreferences to DataStore
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.*
import androidx.datastore.preferences.preferencesDataStore
import kotlinx.coroutines.flow.*
val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings")
// SharedPreferences usage (legacy)
val prefs = getSharedPreferences("settings", Context.MODE_PRIVATE)
val value = prefs.getString("key", "default")
// DataStore usage (modern)
val valueFlow: Flow<String> = dataStore.data
.map { preferences ->
preferences[STRING_PREFERENCES_KEY] ?: "default"
}
Note: DataStore is the recommended approach for modern Android development due to its asynchronous nature and type safety.