Storing Data with SharedPreferences
Storing Data with SharedPreferences
Almost every Android app needs to remember something between sessions: whether the user has completed onboarding, their chosen theme, an authentication token, or the last search query they typed. SharedPreferences is Android's built-in key-value store for exactly this kind of lightweight, persistent data. It stores primitive types and strings in an XML file on the device's internal storage, survives process death and device reboots, and requires zero setup code.
What SharedPreferences Is (and Is Not)
Think of SharedPreferences as a typed map that is automatically flushed to disk. It supports six value types: String, int, long, float, boolean, and Set<String>. That is its entire scope. It is not a relational database, not a file store, and not a good fit for large or structured data. If you need to store more than a few dozen key-value pairs, or if the data has relationships, use Room instead (covered in Lesson 3).
/data/data/<your.package.name>/shared_prefs/<filename>.xml. It is private to your app — other apps cannot read it. The file is not encrypted by default, so never store passwords or sensitive tokens here without additional protection.
Getting a SharedPreferences Instance
There are two ways to obtain a SharedPreferences object. The first is getSharedPreferences(name, mode), which lets you name the file. The second is getPreferences(mode), available only inside an Activity, which uses the activity's class name as the file name.
Always pass Context.MODE_PRIVATE as the mode. The other modes (MODE_WORLD_READABLE, MODE_WORLD_WRITEABLE) were deprecated in API 17 and removed in API 24 because they are security vulnerabilities. MODE_PRIVATE is the only safe choice.
Reading Values
Every get method on SharedPreferences takes a key and a default value that is returned when the key does not exist. Always supply a sensible default — it removes the need for null checks and makes the intent of the code clear.
Writing Values with Editor
You cannot write directly through a SharedPreferences object. Instead you open an Editor, make your changes, and then commit them. This transactional design means either all your changes land on disk or none of them do.
apply() writes to an in-memory cache immediately and schedules the disk write on a background thread. commit() blocks the calling thread until the disk write completes and returns a boolean indicating success. Use apply() on the main thread (which is the vast majority of cases). Use commit() only when you genuinely need confirmation that the data reached disk before continuing — for example, just before the process might be killed in a critical operation.
You can also chain editor calls using the fluent API:
Removing and Clearing Data
Listening for Changes
Your UI sometimes needs to react when a preference changes — for example, switching between light and dark theme. Register an OnSharedPreferenceChangeListener and unregister it when the component is destroyed to avoid memory leaks.
SharedPreferences stores listeners in a WeakHashMap. If you pass an anonymous lambda or a local variable, it may be garbage-collected almost immediately and your listener will silently stop firing. Always store the listener as an instance field or implement the interface on the Activity/Fragment class directly, as shown above.
A Practical Pattern: Preferences Helper Class
Spreading magic string keys across multiple classes is a maintenance hazard. Centralise all preference access in a dedicated helper so keys are defined once and callers never see raw strings.
Callers only interact with well-typed methods and never know about the underlying string keys.
When to Use SharedPreferences
- User preferences — theme, language, notification settings.
- Session flags — has the user seen the onboarding screen, is the user logged in.
- Simple cached values — the last known city for a weather app, the last selected tab.
- Small app-state snippets — a user ID string, a preference integer.
Do not use it for: large datasets, lists of objects, binary data, or anything that benefits from querying. For those cases, Room is the right tool.
Summary
SharedPreferences provides a simple, durable key-value store backed by an XML file. Obtain an instance with getSharedPreferences("name", Context.MODE_PRIVATE), read values with typed get methods that accept defaults, and write through an Editor finished with apply(). Keep string keys out of callers by wrapping the preferences in a dedicated helper class. In the next lesson you will move to SQLite for data that needs structure and queryability.