SharedPreferences & Data Storage — Saving User Data
Every app needs to store data. For simple data like user preferences, settings, theme choices, or login state, SharedPreferences is the perfect solution. It's a lightweight key-value storage system that persists data across app restarts.
1. What is SharedPreferences?
SharedPreferences is an Android API that allows you to save and retrieve persistent key-value pairs of primitive data types. It's ideal for storing:
- User preferences (theme, language, notification settings)
- App settings (first-time launch, onboarding status)
- Login credentials (email, user ID, authentication token)
- Game scores and levels
- Any small amount of simple data
| Feature | SharedPreferences |
|---|---|
| Data Type | Key-value pairs (primitive types only) |
| Storage | XML file in app's private directory |
| Access | Private to the app (not accessible by other apps) |
| Persistence | Persists across app restarts and device reboots |
| Size Limit | ~1-2 MB (not for large data) |
| Thread Safety | Thread-safe (multiple threads can read/write safely) |
2. SharedPreferences Modes
| Mode | Description | Use Case |
|---|---|---|
MODE_PRIVATE | Only your app can access the data | ✅ Recommended for all apps |
MODE_MULTI_PROCESS | Accessible from multiple processes (deprecated) | ⚠️ Avoid — use ContentProvider instead |
MODE_WORLD_READABLE | Any app can read (deprecated) | ❌ Never use — security risk |
MODE_WORLD_WRITEABLE | Any app can write (deprecated) | ❌ Never use — security risk |
MODE_PRIVATE for SharedPreferences. The other modes are deprecated and pose security risks. Use ContentProvider for data sharing between apps.
3. Getting Started with SharedPreferences
Step 1: Get SharedPreferences Instance
// Method 1: Get default preferences (shared across app)
val sharedPref = PreferenceManager.getDefaultSharedPreferences(context)
// Method 2: Get named preferences (separate file)
val sharedPref = context.getSharedPreferences("MyAppPrefs", Context.MODE_PRIVATE)
Step 2: Write Data
// Write data to SharedPreferences
val sharedPref = getSharedPreferences("MyAppPrefs", Context.MODE_PRIVATE)
val editor = sharedPref.edit()
editor.putString("user_name", "John Doe")
editor.putInt("user_age", 25)
editor.putBoolean("notifications_enabled", true)
editor.putFloat("user_rating", 4.5f)
editor.putLong("last_login_time", System.currentTimeMillis())
editor.apply() // Asynchronous write (recommended)
// OR editor.commit() // Synchronous write (slower, but returns result)
Step 3: Read Data
// Read data from SharedPreferences
val sharedPref = getSharedPreferences("MyAppPrefs", Context.MODE_PRIVATE)
val userName = sharedPref.getString("user_name", "Guest")
val userAge = sharedPref.getInt("user_age", 0)
val notificationsEnabled = sharedPref.getBoolean("notifications_enabled", false)
val userRating = sharedPref.getFloat("user_rating", 0.0f)
val lastLoginTime = sharedPref.getLong("last_login_time", 0L)
// Check if a key exists
val hasUserName = sharedPref.contains("user_name")
// Get all keys (for debugging)
val allEntries = sharedPref.all
Step 4: Remove Data
// Remove a specific key
val editor = sharedPref.edit()
editor.remove("user_age")
editor.apply()
// Clear all data
editor.clear()
editor.apply()
4. Complete Example — User Settings App
Here's a complete example of a settings screen using SharedPreferences:
Setting Up Preferences
// PreferenceKeys.kt — Centralized key management
object PreferenceKeys {
const val PREF_NAME = "AppSettings"
const val KEY_THEME = "theme_mode"
const val KEY_NOTIFICATIONS = "notifications_enabled"
const val KEY_USER_NAME = "user_name"
const val KEY_USER_EMAIL = "user_email"
const val KEY_FIRST_LAUNCH = "first_launch"
const val KEY_LAST_OPENED = "last_opened"
}
// PreferenceManager.kt — Utility class for SharedPreferences
class PreferenceManager(context: Context) {
private val sharedPref = context.getSharedPreferences(
PreferenceKeys.PREF_NAME,
Context.MODE_PRIVATE
)
// Theme preference
fun getThemeMode(): String {
return sharedPref.getString(PreferenceKeys.KEY_THEME, "light") ?: "light"
}
fun setThemeMode(theme: String) {
sharedPref.edit().putString(PreferenceKeys.KEY_THEME, theme).apply()
}
// Notifications preference
fun areNotificationsEnabled(): Boolean {
return sharedPref.getBoolean(PreferenceKeys.KEY_NOTIFICATIONS, true)
}
fun setNotificationsEnabled(enabled: Boolean) {
sharedPref.edit().putBoolean(PreferenceKeys.KEY_NOTIFICATIONS, enabled).apply()
}
// User name
fun getUserName(): String {
return sharedPref.getString(PreferenceKeys.KEY_USER_NAME, "") ?: ""
}
fun setUserName(name: String) {
sharedPref.edit().putString(PreferenceKeys.KEY_USER_NAME, name).apply()
}
// User email
fun getUserEmail(): String {
return sharedPref.getString(PreferenceKeys.KEY_USER_EMAIL, "") ?: ""
}
fun setUserEmail(email: String) {
sharedPref.edit().putString(PreferenceKeys.KEY_USER_EMAIL, email).apply()
}
// First launch check
fun isFirstLaunch(): Boolean {
return sharedPref.getBoolean(PreferenceKeys.KEY_FIRST_LAUNCH, true)
}
fun setFirstLaunch(isFirst: Boolean) {
sharedPref.edit().putBoolean(PreferenceKeys.KEY_FIRST_LAUNCH, isFirst).apply()
}
// Last opened timestamp
fun getLastOpened(): Long {
return sharedPref.getLong(PreferenceKeys.KEY_LAST_OPENED, 0L)
}
fun setLastOpened(timestamp: Long) {
sharedPref.edit().putLong(PreferenceKeys.KEY_LAST_OPENED, timestamp).apply()
}
// Clear all preferences (logout)
fun clearAll() {
sharedPref.edit().clear().apply()
}
}
Settings Activity
// SettingsActivity.kt
class SettingsActivity : AppCompatActivity() {
private lateinit var preferenceManager: PreferenceManager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_settings)
preferenceManager = PreferenceManager(this)
// Load saved preferences
loadPreferences()
// Setup listeners
setupListeners()
}
private fun loadPreferences() {
// Load theme
val theme = preferenceManager.getThemeMode()
// Apply theme and update UI
// Load notifications setting
val notificationsEnabled = preferenceManager.areNotificationsEnabled()
// Update switch UI
// Load user name
val userName = preferenceManager.getUserName()
// Update name field
// Load user email
val userEmail = preferenceManager.getUserEmail()
// Update email field
}
private fun setupListeners() {
// Theme change listener
findViewById(R.id.themeGroup).setOnCheckedChangeListener { _, checkedId ->
val theme = when (checkedId) {
R.id.themeLight -> "light"
R.id.themeDark -> "dark"
R.id.themeSystem -> "system"
else -> "light"
}
preferenceManager.setThemeMode(theme)
// Apply theme
}
// Notifications toggle
findViewById(R.id.notificationsSwitch).setOnCheckedChangeListener { _, isChecked ->
preferenceManager.setNotificationsEnabled(isChecked)
}
// Save name
findViewById
5. EncryptedSharedPreferences — Secure Storage
For sensitive data (passwords, tokens, personal information), use EncryptedSharedPreferences to encrypt the data.
Setup
Add dependency to your build.gradle:
dependencies {
implementation 'androidx.security:security-crypto:1.1.0-alpha06'
}
Implementation
import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKeys
// Create encrypted shared preferences
val masterKeyAlias = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC)
val encryptedPref = EncryptedSharedPreferences.create(
"secure_prefs",
masterKeyAlias,
context,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
// Use the encrypted preferences (same methods as regular SharedPreferences)
encryptedPref.edit().putString("auth_token", "secure_token_123").apply()
val token = encryptedPref.getString("auth_token", null)
6. SharedPreferences vs Other Storage Options
| Storage Type | Use Case | Size Limit | Complexity |
|---|---|---|---|
| SharedPreferences | Simple key-value data, settings, user preferences | ~1-2 MB | Low |
| SQLite / Room | Structured relational data, large datasets | Unlimited | Medium |
| Internal Storage | Files, images, documents | Unlimited | Medium |
| External Storage | Public files, shareable content | Unlimited | Medium |
| DataStore | Modern alternative to SharedPreferences | ~1-2 MB | Low |
7. DataStore — The Modern Alternative
DataStore is the modern replacement for SharedPreferences, offering better performance, type safety, and support for coroutines and Flow.
Preferences DataStore
// Add dependency
// implementation "androidx.datastore:datastore-preferences:1.0.0"
// Create DataStore
val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "user_prefs")
// Define keys
val THEME_KEY = stringPreferencesKey("theme")
val NOTIFICATIONS_KEY = booleanPreferencesKey("notifications")
// Write data (suspend function)
suspend fun savePreferences(dataStore: DataStore<Preferences>) {
dataStore.edit { preferences ->
preferences[THEME_KEY] = "dark"
preferences[NOTIFICATIONS_KEY] = true
}
}
// Read data (Flow)
val themeFlow: Flow<String> = dataStore.data.map { preferences ->
preferences[THEME_KEY] ?: "light"
}
8. Common Pitfalls & Solutions
Pitfall 1: Storing large amounts of data
Problem: SharedPreferences is designed for small amounts of data (~1-2 MB). Storing large data can cause performance issues.
Solution: Use SQLite, Room, or internal storage for large data.
Pitfall 2: Not using apply() vs commit() correctly
Problem: commit() writes synchronously on the main thread, causing UI lag.
Solution: Use apply() for asynchronous writes. Use commit() only when you need the result immediately.
// ✅ Good — asynchronous
editor.apply()
// ❌ Bad — synchronous (blocks UI thread)
editor.commit()
Pitfall 3: Storing sensitive data in plain text
Problem: SharedPreferences stores data in plain XML files, visible on rooted devices.
Solution: Use EncryptedSharedPreferences for sensitive data.
Pitfall 4: Not handling default values
Problem: Accessing a key that doesn't exist returns null or default.
Solution: Always provide a default value when reading.
// ✅ Always provide default values
val name = sharedPref.getString("user_name", "Guest")
val age = sharedPref.getInt("user_age", 0)
9. Production-Ready Checklist
- ✅ Use MODE_PRIVATE — Never use other modes (deprecated/unsafe).
- ✅ Use apply() over commit() — For asynchronous writes.
- ✅ Provide default values — Always when reading from SharedPreferences.
- ✅ Use EncryptedSharedPreferences — For sensitive data.
- ✅ Centralize keys — Use an object/class with constants for keys.
- ✅ Consider DataStore — For new projects as a modern alternative.
- ✅ Don't store large data — Use SQLite/Room for large datasets.
- ✅ Test persistence — Verify data survives app restarts.
- ✅ Handle first launch — Use a flag to detect first app launch.
- ✅ Clear on logout — Clear preferences when user logs out.
- ✅ Use appropriate data types — Choose the right type for your data.
- ✅ Backup & restore — Consider implementing backup for user preferences.
10. Quick Reference — SharedPreferences Methods
| Method | Description |
|---|---|
getString(key, defaultValue) | Retrieve a String value |
getInt(key, defaultValue) | Retrieve an Int value |
getBoolean(key, defaultValue) | Retrieve a Boolean value |
getFloat(key, defaultValue) | Retrieve a Float value |
getLong(key, defaultValue) | Retrieve a Long value |
putString(key, value) | Store a String value |
putInt(key, value) | Store an Int value |
putBoolean(key, value) | Store a Boolean value |
remove(key) | Remove a specific key |
clear() | Remove all keys |
contains(key) | Check if a key exists |
all | Get all key-value pairs |
Ready to master Android Development?
Build real-world Android apps with hands-on projects, mentor-led sessions, and placement support.
.png)