Back to Course
Data Storage

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.

Production Reality: SharedPreferences is one of the most commonly used Android features. Almost every app uses it to store user preferences, app settings, and session data. It's simple, fast, and reliable for small amounts of data.

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
FeatureSharedPreferences
Data TypeKey-value pairs (primitive types only)
StorageXML file in app's private directory
AccessPrivate to the app (not accessible by other apps)
PersistencePersists across app restarts and device reboots
Size Limit~1-2 MB (not for large data)
Thread SafetyThread-safe (multiple threads can read/write safely)

2. SharedPreferences Modes

ModeDescriptionUse Case
MODE_PRIVATEOnly your app can access the data✅ Recommended for all apps
MODE_MULTI_PROCESSAccessible from multiple processes (deprecated)⚠️ Avoid — use ContentProvider instead
MODE_WORLD_READABLEAny app can read (deprecated)❌ Never use — security risk
MODE_WORLD_WRITEABLEAny app can write (deprecated)❌ Never use — security risk
Production Warning: Only use 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)
Pro Tip: Use EncryptedSharedPreferences for storing sensitive data like authentication tokens, passwords, or personal user information. Regular SharedPreferences stores data as plain text XML, which can be accessed on rooted devices.

6. SharedPreferences vs Other Storage Options

Storage TypeUse CaseSize LimitComplexity
SharedPreferencesSimple key-value data, settings, user preferences~1-2 MBLow
SQLite / RoomStructured relational data, large datasetsUnlimitedMedium
Internal StorageFiles, images, documentsUnlimitedMedium
External StoragePublic files, shareable contentUnlimitedMedium
DataStoreModern alternative to SharedPreferences~1-2 MBLow

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"
}
Pro Tip: For new projects, consider using DataStore instead of SharedPreferences. It provides better performance, type safety, and coroutine support. However, SharedPreferences is still widely used and perfectly fine for simpler use cases.

8. Common Pitfalls & Solutions

Common Mistakes to Avoid:

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

MethodDescription
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
allGet all key-value pairs
Key Takeaway: SharedPreferences is the go-to solution for storing simple key-value data in Android. It's fast, reliable, and easy to use. For sensitive data, use EncryptedSharedPreferences. For new projects, consider DataStore as a modern alternative. Always centralize your preference keys and provide default values when reading.

Ready to master Android Development?

Build real-world Android apps with hands-on projects, mentor-led sessions, and placement support.

Explore Course