๐Ÿ”Œ APIs, Databases, and Backend Integration

Room vs DataStore: Which Should You Use?

Room and DataStore both persist data on the device, but they solve different problems. Room is a relational database. DataStore stores small, simple values or typed objects. Pick by the shape and size of your data.

โœ๏ธ Yunok Tech ๐Ÿ“… Updated โฑ 3 min read ๐ŸŽฏ Intermediate
On this page
  1. Quick answer
  2. DataStore
  3. Room
  4. Side-by-side
  5. Migrating from SharedPreferences
  6. Examples
  7. Common mistakes
  8. Frequently asked questions
  9. Conclusion

"Where do I save this?" comes up constantly. A theme preference, a list of 5,000 transactions, the last-opened tab, an onboarding flag, cached articles. Using a database for one boolean is overkill. Using key-value storage for thousands of records is a mistake. Here's how to choose.

Quick answer

DataUse
Settings and flags (theme, onboarding done, last tab)Preferences DataStore
A small typed settings object with schemaProto DataStore (or DataStore with a serializer)
Collections, records, anything you query, sort or relateRoom
Large binary files (images, PDFs)Files in app storage, with paths in Room
Secrets (tokens, keys)Encrypted storage backed by the Android Keystore. See Secure Token Storage
Decision guide between DataStore and Room Is it many records you query or relate? Use Room. Is it a few simple values? Use Preferences DataStore. Is it a small typed object needing schema and type safety? Use Proto DataStore. Many records, queries, relations? yesno Room Preferencessimple keys Proto/typedschema object
Shape and size decide. Many apps use both: DataStore for settings, Room for content.

DataStore

DataStore replaces SharedPreferences. It's asynchronous (Flow and suspend), consistent (transactional updates), and safe to call from the UI thread because I/O happens off the main thread. It's designed for small datasets, and every update rewrites the whole file.

SettingsRepository.kt (Preferences DataStore)Kotlin
private val Context.settingsStore by preferencesDataStore(name = "settings")   // one instance per file

enum class ThemeMode { SYSTEM, LIGHT, DARK }
data class Settings(val theme: ThemeMode, val onboardingDone: Boolean)

class SettingsRepository(private val context: Context) {
    private object Keys {
        val THEME = stringPreferencesKey("theme")
        val ONBOARDED = booleanPreferencesKey("onboarding_done")
    }

    val settings: Flow<Settings> = context.settingsStore.data
        .catch { e -> if (e is IOException) emit(emptyPreferences()) else throw e }   // corrupted/unreadable file
        .map { p ->
            Settings(
                theme = p[Keys.THEME]?.let { runCatching { ThemeMode.valueOf(it) }.getOrNull() } ?: ThemeMode.SYSTEM,
                onboardingDone = p[Keys.ONBOARDED] ?: false,
            )
        }

    suspend fun setTheme(mode: ThemeMode) = context.settingsStore.edit { it[Keys.THEME] = mode.name }
    suspend fun completeOnboarding() = context.settingsStore.edit { it[Keys.ONBOARDED] = true }
}

Room

Room is a full SQLite database: indexed queries, relations, partial updates, paging, and observation per table. Use it whenever data is a collection, or you need to filter or sort. See the Room tutorial.

Side-by-side

AspectDataStoreRoom
Data modelKey-value or one typed objectTables, rows, relations
QueriesNone (read everything, map in Kotlin)SQL with compile-time checks
Partial updatesNo: rewrites the fileYes
SizeSmall (settings)Large datasets
ObservationFlow of whole dataFlow per query
MigrationsKey or schema evolution; SharedPreferences migration helperVersioned SQL migrations

Migrating from SharedPreferences

One-time migrationKotlin
private val Context.settingsStore by preferencesDataStore(
    name = "settings",
    produceMigrations = { context -> listOf(SharedPreferencesMigration(context, "legacy_prefs")) },
)

Examples

Notes app

Room for notes and folders, DataStore for sort order and theme.

Shopping app

Room for cached catalogue and cart, DataStore for recent search terms (small, capped list) and preferences.

Fitness app

Room for workout history (queried by date range), DataStore for units (km or miles) and goals.

Common mistakes

  • Storing large lists as JSON strings in DataStore or SharedPreferences.
  • Creating multiple DataStore instances for the same file, which throws an error at runtime.
  • Blocking reads with runBlocking { dataStore.data.first() } on the main thread.
  • Putting secrets in plain DataStore.

Frequently asked questions

Is SharedPreferences deprecated?

It isn't removed, but Google recommends DataStore for new code because SharedPreferences has synchronous APIs that can block the UI thread and weaker consistency guarantees.

Should I use Proto DataStore?

Use it when you want a schema and type safety for a settings object. Preferences DataStore is simpler for a handful of keys.

Can DataStore be used in Kotlin Multiplatform?

Yes. DataStore supports KMP targets in recent versions.

Conclusion

DataStore for small settings and flags, Room for collections you query, files for large binaries, and Keystore-backed encryption for secrets. Most production apps use more than one, each for what it does best.

Next step: combine local and remote data in Offline-First Android App Architecture.

Official references

๐Ÿ“ฑ Apps built by Yunok Tech

Real Android apps on Google Play, free to download.

See all our apps โ†’

Ready to build real Android applications?

Networking, persistence and offline sync are where real apps get complicated. Practise building a data layer that handles slow networks, failures and caching, not just the happy path.

Move beyond theory and strengthen your Android development skills with Yunoktech's Android course. Learn core concepts, practical implementation, modern Android development techniques, and the reasoning behind building maintainable applications.

Explore the Android course and take your next step as an Android developer.

Share: LinkedIn X / Twitter WhatsApp