Skip to content

Room, DataStore and WorkManager Interview Questions for Android App Development

25 Room, DataStore and WorkManager questions with answers for android app development roles — 7 easy, 13 medium, 5 hard.

About Room, DataStore and WorkManager

Most real apps need to store data locally and sync it in the background. Learn Room entities, DAOs, relations and migrations, DataStore for preferences, secure storage with Keystore, Paging 3, scoped storage and WorkManager for guaranteed background work. Offline-first design questions are common in product company interviews in India.

Sheet progressStart practicing
3 QUESTIONS FREESign up to unlock the full question bank and track your progress.Unlock every question and answer, then continue practising company-wise and package-wise.
3 questions free Unlock the rest in seconds
01

What is Room and what are its main components?

Save to ▾
Show answer
Room is the Jetpack persistence library built on SQLite. Its main parts are @Entity classes that map to tables, @Dao interfaces with queries annotated by @Query, @Insert, @Update and @Delete, and an abstract @Database class that ties them together. Room verifies SQL at compile time, supports suspend functions and Flow return types, and handles migrations. It removes most SQLite boilerplate while keeping full SQL power.
02

Write a Room entity and DAO for notes with a Flow query.

Save to ▾
Show answer
@Entity(tableName = "notes") data class NoteEntity( @PrimaryKey(autoGenerate = true) val id: Long = 0, val title: String, val body: String, val updatedAt: Long ) @Dao interface NoteDao { @Query("SELECT * FROM notes ORDER BY updatedAt DESC") fun observeAll(): Flow<List<NoteEntity>> @Upsert suspend fun upsert(note: NoteEntity) @Query("DELETE FROM notes WHERE id = :id") suspend fun delete(id: Long) }
03

What is DataStore and why does it replace SharedPreferences?

Save to ▾
Show answer
Jetpack DataStore stores key-value pairs (Preferences DataStore) or typed objects (Proto DataStore) using coroutines and Flow. Unlike SharedPreferences, it is safe to call from the main thread because I/O is asynchronous, it reports errors through exceptions, and updates are transactional. SharedPreferences apply() can still block the main thread during lifecycle events and cause ANRs. Use one DataStore instance per file, ideally as a singleton.
04

Write code to save and read a dark mode preference with Preferences DataStore.

Save to ▾
Show answer
val Context.dataStore by preferencesDataStore(name = "settings") private val DARK_MODE = booleanPreferencesKey("dark_mode") class SettingsRepository(private val context: Context) { val darkMode: Flow<Boolean> = context.dataStore.data .catch { emit(emptyPreferences()) } .map { it[DARK_MODE] ?: false } suspend fun setDarkMode(enabled: Boolean) { context.dataStore.edit { it[DARK_MODE] = enabled } } }
05

What is WorkManager and when should you use it?

Save to ▾
Show answer
WorkManager schedules deferrable, guaranteed background work that must run even if the app exits or the device restarts, such as syncing data, uploading logs or backing up photos. It supports constraints like network or charging, retries with backoff, chaining and unique work. It is not for exact-time tasks or work that must finish immediately while the user waits; use coroutines for that and AlarmManager for exact alarms.
06

How do you prioritise when you have several bugs and a feature deadline in the same week?

Save to ▾
Show answer
I rank by user impact and risk: crashes and data loss first, then bugs affecting payments or login, then the feature, then minor UI issues. I share the plan with my lead and product manager so trade-offs are visible, and I ask for help or a scope cut early instead of quietly missing the deadline. Small, safe fixes I batch together to save release overhead.
07

A user complains the app uses too much storage. How do you find and reduce it?

Save to ▾
Show answer
Check Settings storage breakdown and inspect filesDir, cacheDir and databases with Device Explorer. Common culprits are unbounded image caches, old downloaded files, logs, and databases that never delete old rows. Set size limits on Coil and OkHttp caches, use cacheDir for temporary files so the system can clear them, delete old records with a periodic WorkManager job, and compress or downscale stored images.
08

How do Room migrations work?

Save to ▾
Show answer
When you change the schema, increase the database version and provide a Migration(from, to) with SQL like ALTER TABLE, or use AutoMigration for simple changes with exported schemas. Without a migration, Room throws at startup unless you call fallbackToDestructiveMigration, which wipes user data. Export schemas to version control and test migrations with MigrationTestHelper so upgrades from every old version are verified.
09

Write a periodic sync job with WorkManager that runs only on unmetered network.

Save to ▾
Show answer
class SyncWorker(ctx: Context, params: WorkerParameters) : CoroutineWorker(ctx, params) { override suspend fun doWork(): Result = try { repo.sync() Result.success() } catch (e: IOException) { if (runAttemptCount < 3) Result.retry() else Result.failure() } } val request = PeriodicWorkRequestBuilder<SyncWorker>(6, TimeUnit.HOURS) .setConstraints(Constraints(requiredNetworkType = NetworkType.UNMETERED)) .setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 30, TimeUnit.SECONDS) .build() WorkManager.getInstance(context).enqueueUniquePeriodicWork( "sync", ExistingPeriodicWorkPolicy.KEEP, request)
10

How do you store relationships in Room, such as orders with items?

Save to ▾
Show answer
Create separate entities and link them with a foreign key column, like OrderItem.orderId. Then define a data class with @Embedded order and @Relation(parentColumn = "id", entityColumn = "orderId") items, and query it in a DAO method annotated with @Transaction so both reads are consistent. For many-to-many relations, add a cross-reference entity with junction. Add indices on foreign key columns for query speed.
11

After an app update, users report that the app crashes on launch. Logs show a Room migration error. What do you do?

Save to ▾
Show answer
Immediately halt the staged rollout in Play Console. Reproduce by installing the previous version, adding data, and upgrading. The schema version was probably increased without a matching migration, or a migration SQL does not match the new entity. Write the correct Migration, test it with MigrationTestHelper from every supported old version, and release a hotfix. Avoid destructive fallback because it deletes user data.
12

A Room query on a large table is slow and causes jank. How do you fix it?

Save to ▾
Show answer
Make sure the query runs off the main thread using suspend or Flow. Use EXPLAIN QUERY PLAN to check for full table scans, and add an @Index on columns used in WHERE, JOIN or ORDER BY. Select only needed columns into a smaller data class instead of SELECT *, page results with Paging 3, and avoid loading large blobs into lists. Wrap bulk inserts in a single transaction.
13

You need to cache API responses so the home screen opens instantly. What approach do you take?

Save to ▾
Show answer
Store the response in Room, or a file for a single blob, and have the screen observe the cached data immediately while a refresh runs in the background, known as stale-while-revalidate. Store a timestamp to decide when cache is too old. HTTP caching with OkHttp Cache can also help for GET requests with proper cache headers. Show cached content first, then update smoothly when fresh data arrives.
14

Users who reinstall the app lose all their settings. How can you restore them?

Save to ▾
Show answer
Android Auto Backup automatically backs up app data to the user's cloud backup account for apps targeting API 23 and above, unless disabled. Configure dataExtractionRules for Android 12 and above to include settings files and exclude tokens or caches. For important data like notes, sync to your own server tied to the user's account, since backup is not guaranteed. Test with adb shell bmgr commands.
15

How do you write tests for a Room DAO?

Save to ▾
Show answer
Create an in-memory database with Room.inMemoryDatabaseBuilder in an instrumented test or with Robolectric, get the DAO, insert test data and assert query results. For Flow queries, collect the first value with first() or use Turbine. Close the database in @After. Test edge cases like conflict strategies and ordering. In-memory databases are fast and isolated, so each test starts clean.
16

What file storage options does Android provide and how does scoped storage affect them?

Save to ▾
Show answer
App-specific storage (filesDir, cacheDir, getExternalFilesDir) needs no permission and is deleted on uninstall. Shared media is accessed through MediaStore, and other documents through the Storage Access Framework. Since Android 10, scoped storage blocks broad file system access, and since Android 13 media permissions are split into images, video and audio. The photo picker lets users choose media without any permission, which is preferred.
17

What conflict strategies does Room offer for inserts?

Save to ▾
Show answer
@Insert(onConflict = ...) supports REPLACE, which deletes the old row and inserts the new one; IGNORE, which skips the insert; ABORT, the default, which rolls back the transaction; and others. REPLACE can trigger cascading deletes on foreign keys because it deletes first. @Upsert, added in Room 2.5, inserts or updates without deleting, making it safer for syncing server data into local tables.
18

How do you migrate from SharedPreferences to DataStore without losing user data?

Save to ▾
Show answer
Use SharedPreferencesMigration when creating the DataStore: preferencesDataStore(name = "settings", produceMigrations = { ctx -> listOf(SharedPreferencesMigration(ctx, "old_prefs")) }). On first access, DataStore copies the values and deletes the old file. Update all reads and writes to go through the new repository at once, because mixing both stores leads to inconsistent values. Test the migration with existing preference files.
19

Write a Room database class with a migration that adds a column.

Save to ▾
Show answer
@Database(entities = [NoteEntity::class], version = 2, exportSchema = true) abstract class AppDatabase : RoomDatabase() { abstract fun noteDao(): NoteDao } val MIGRATION_1_2 = object : Migration(1, 2) { override fun migrate(db: SupportSQLiteDatabase) { db.execSQL("ALTER TABLE notes ADD COLUMN pinned INTEGER NOT NULL DEFAULT 0") } } val db = Room.databaseBuilder(context, AppDatabase::class.java, "app.db") .addMigrations(MIGRATION_1_2) .build()
20

Tell me about a time a bug you introduced reached production. How did you handle it?

Save to ▾
Show answer
Situation: a migration I wrote missed a default value, and some users lost their saved addresses. Task: fix it and regain trust. Action: I told my lead immediately, we paused the rollout, I wrote a correct migration with a test covering upgrades from the last three versions, and we restored data from the server for affected users. Result: impact stayed small, and migration tests became mandatory in our CI.
21

How do you store sensitive data such as tokens securely on Android?

Save to ▾
Show answer
Never store secrets in plain SharedPreferences or hardcode them in code. Use the Android Keystore to generate a key that never leaves secure hardware, and encrypt the data before storing it in DataStore or a file. The security-crypto EncryptedSharedPreferences library is deprecated, so many teams use Tink with Keystore. Keep tokens short-lived with refresh tokens, and clear them on logout. Exclude them from backups.
22

What is the Paging 3 library and how does it work with Room?

Save to ▾
Show answer
Paging 3 loads large datasets in pages to save memory and network. A PagingSource loads pages, Pager exposes Flow<PagingData>, and the UI uses LazyPagingItems in Compose or PagingDataAdapter in RecyclerView. With a RemoteMediator, the network fills Room and Room acts as the PagingSource, giving offline support and a single source of truth. Use cachedIn(viewModelScope) to survive configuration changes.
23

How would you design an offline-first notes app?

Save to ▾
Show answer
Room is the single source of truth: the UI observes DAO flows and writes go to Room immediately with a sync status column like pending. A WorkManager job uploads pending changes when the network is available and pulls server changes, updating Room. Handle conflicts with timestamps or server versions, use unique work to avoid duplicate syncs, and show a small sync indicator. The app works fully without network.
24

A WorkManager job is not running on some phones from certain manufacturers. How do you investigate?

Save to ▾
Show answer
Some manufacturers apply aggressive battery optimisations that delay or kill background work. Check the job state with WorkManager's getWorkInfosForUniqueWorkFlow and the background task inspector in Android Studio. Verify constraints are not too strict. Guide users to allow unrestricted battery for the app only where the feature truly needs it, use expedited work for urgent tasks, and use FCM to trigger sync when timely delivery matters.
25

Explain how you would use Room with Kotlin Multiplatform.

Save to ▾
Show answer
Since Room 2.7, Room supports Kotlin Multiplatform for Android, iOS and desktop. You define entities, DAOs and the database in commonMain, use the bundled SQLite driver, and create the database builder in platform-specific source sets with expect and actual. DAO functions must be suspend or return Flow in common code. This lets teams share the data layer while keeping native UIs, which some Indian product companies are adopting.

Frequently asked questions

How important is Room, DataStore and WorkManager in Android App Development interviews?

Most real apps need to store data locally and sync it in the background. Learn Room entities, DAOs, relations and migrations, DataStore for preferences, secure storage with Keystore, Paging 3, scoped storage and WorkManager for guaranteed background work. Offline-first design questions are common in product company interviews in India.

How should I use the Room, DataStore and WorkManager sheet?

Go top to bottom — easy to hard. Answer before opening each answer, tick what you solved and save misses to Last-Minute Revision.