DataStore & Preferences Quiz
DATA › Storage
Which statement about SharedPreferences.apply() is correct?
- It writes to disk synchronously and returns true when the write succeeds
- It updates memory immediately and writes to disk asynchronously, returning void
- It throws if you call it off the main thread, instead of running in background
- It returns a Boolean that tells you whether the disk write completed successfully
Answer: It updates memory immediately and writes to disk asynchronously, returning void
apply() commits the change to the in-memory map right away and schedules the disk write in the background, returning void. commit() is the synchronous variant that returns a Boolean.
What is the key advantage of Proto DataStore over Preferences DataStore?
- It works without adding any extra library dependencies at all
- It stores data in plain XML without any encryption or encoding
- It uses a defined schema to provide compile-time type safety
- It allows synchronous blocking reads directly on the main thread
Answer: It uses a defined schema to provide compile-time type safety
Proto DataStore uses a .proto schema to generate typed classes, giving compile-time type safety. Preferences DataStore is untyped key-value like SharedPreferences.
How are reads exposed in DataStore?
- As a Flow<T> via the data property
- As a synchronous getter that blocks the caller
- As a LiveData<T> only
- Through a callback registered with a listener
Answer: As a Flow<T> via the data property
DataStore exposes its data as a Flow<T> (dataStore.data), enabling asynchronous, reactive, non-blocking reads. Writes are suspend functions.
Which class is used to migrate existing SharedPreferences data into DataStore?
- PreferenceMigrationHelper
- DataStoreConverter
- SharedPreferencesMigration
- MigrationManager.fromPrefs()
Answer: SharedPreferencesMigration
SharedPreferencesMigration is supplied in the migrations list when building the DataStore; it copies values across on first access and then removes the original SharedPreferences file by default.
What does the suspend dataStore.edit { } block guarantee for Preferences DataStore?
- It runs the block as a single atomic transaction
- It blocks the main thread until the write completes
- It silently discards errors during the write
- It only updates an in-memory cache and never persists
Answer: It runs the block as a single atomic transaction
The edit block executes as a single atomic, transactional update; all changes are applied and persisted together, preserving read-after-write consistency.
What is the current recommendation regarding EncryptedSharedPreferences in modern Android?
- It is the mandatory default for all key-value storage on Android
- It is deprecated and not the recommended choice for new code
- It must be used before you can adopt DataStore in new projects
- It encrypts every SharedPreferences file automatically in the app
Answer: It is deprecated and not the recommended choice for new code
EncryptedSharedPreferences from androidx.security:security-crypto is deprecated. Google now advises relying on platform disk encryption plus the Keystore or maintained crypto libraries instead.
If a DataStore file becomes corrupted, what is the correct way to recover gracefully?
- Catch IllegalStateException in each read and ignore the bad file
- Call commit() to overwrite the file after a corruption is detected
- Wrap reads in runBlocking and keep retrying until the file works
- Pass a ReplaceFileCorruptionHandler when building the DataStore
Answer: Pass a ReplaceFileCorruptionHandler when building the DataStore
A corrupted file causes a CorruptionException; supplying a ReplaceFileCorruptionHandler lets DataStore replace the bad file with a default value rather than crashing.
What is the recommended way to obtain a Preferences DataStore instance in modern Android?
- Call DataStoreFactory.create() fresh in every function that needs it
- Use the preferencesDataStore delegate once at a file’s top level
- Instantiate a new PreferenceManager in each Activity's onCreate
- Call context.getSharedPreferences().toDataStore() for the file
Answer: Use the preferencesDataStore delegate once at a file’s top level
The preferencesDataStore delegate creates and caches a single DataStore for a given file name, which is the recommended pattern; building new instances per call risks violating the one-instance-per-file rule.
What happens if you create two DataStore instances backed by the same file within one process?
- The second creation throws an IllegalStateException
- Both instances transparently share the same cache with no downside
- Writes are silently merged using a last-write-wins strategy
- The first instance becomes read-only and the second takes over writes
Answer: The second creation throws an IllegalStateException
DataStore enforces a single active instance per file per process to guarantee atomicity and read-after-write consistency; a second instance for the same file throws IllegalStateException.
When setting up a Proto DataStore, what must you supply so it can convert your object to and from bytes on disk?
- A Room TypeConverter-annotated class
- A Serializer<T> implementation
- A Gson instance passed to the builder
- A Parcelable implementation of the type
Answer: A Serializer<T> implementation
Proto DataStore requires a Serializer<T> that defines the default value and how to read/write the type as bytes; this is what gives Proto DataStore its typed, schema-backed persistence.
How do you write a new value to a Proto DataStore?
- Call the suspend updateData { current -> ... } function
- Call the edit { } block exactly as with Preferences DataStore
- Assign directly to dataStore.value on the main thread
- Call commit() on the generated message object
Answer: Call the suspend updateData { current -> ... } function
Proto DataStore writes go through the suspend updateData function, which receives the current value and returns the updated one as a single atomic transaction. edit { } is the Preferences DataStore API.
Which expression correctly creates a key for storing an Int in Preferences DataStore today?
- preferencesKey<Int>("count")
- keyOf<Int>("count")
- intPreferencesKey("count")
- Preferences.intKey("count")
Answer: intPreferencesKey("count")
intPreferencesKey(name) is the current typed-key helper for ints (alongside stringPreferencesKey, booleanPreferencesKey, etc.). The generic preferencesKey<Int>() form was removed in favor of these type-specific factories.
While collecting dataStore.data, an IOException can be thrown if the file cannot be read. What is the idiomatic way to handle it?
- Wrap the entire app-startup collection in a runBlocking try/catch
- Use Flow's catch operator to emit emptyPreferences()
- Nothing is needed, since DataStore reads can never throw here
- Call commit() inside the collector to reset the corrupt file
Answer: Use Flow's catch operator to emit emptyPreferences()
Read errors surface through the Flow, so the idiomatic fix is the catch operator: on an IOException you emit emptyPreferences() (or a default), while rethrowing other exceptions you can't recover from.
Which scenario is DataStore NOT designed to handle well?
- Storing a small set of user settings and feature flags in one file
- Reactively observing a single preference value as a Flow stream
- Replacing SharedPreferences for simple key-value storage needs
- Large, complex, or relational datasets that need querying
Answer: Large, complex, or relational datasets that need querying
DataStore is for small/simple datasets and rewrites the whole file on each write; large, complex, or queryable relational data should use Room instead.