Room basics

Data and storage Junior Kotlin 2.0+ API 24+ Reviewed By Best Latest Apps

Pick the fix

The app freezes briefly on a list screen that calls a DAO function from the main thread with allowMainThreadQueries(). Best fix?

Options
Hint 1 of 1

Which thread runs the query today?

Choose an answer, then open Show answer below to check it.

Show answer

Answer: A Make the DAO function suspend or return a Flow, and remove allowMainThreadQueries

Correct. Room then runs the query off the main thread.

Why not the others?
  • B Wrap the call in runBlocking(Dispatchers.IO). Still blocks the main thread while it waits.
  • C Add an index to every column. Indexes may help speed, but the query still runs on main.
  • D Use raw SQLite instead. Same problem, more code.

30-second answer

Room is a layer over SQLite with three parts: @Entity classes for tables, @Dao interfaces with annotated queries, and a @Database class. It checks SQL at compile time against your schema, maps rows to Kotlin objects, supports suspend functions and Flow, and manages migrations. It refuses queries on the main thread unless you opt in, which stops a common source of jank.

Practise saying it · 35 s. No recording, just a timer

Explain what Room gives you.

Say your answer out loud, then check which of these points you covered:

  • Entities, DAOs, Database
  • Compile-time SQL checks
  • suspend and Flow support
  • No main-thread queries
  • Schema export for migrations

Deep dive

Return Flow<List<T>> from a DAO to observe a query; Room re-runs it when the tables it reads change.

suspend DAO functions run on Room's own executor, so they're main-safe.

Room uses KSP to generate the DAO code.

Export the schema JSON (room.schemaLocation) and check it into version control for migration tests.

Entity, DAO and database Compile-checked · Kotlin 2.4.21
@Entity(tableName = "notes")
data class NoteRow(@PrimaryKey(autoGenerate = true) val id: Long = 0, val text: String, val updatedAt: Long)

@Dao
interface NotesDao {
    @Query("SELECT * FROM notes ORDER BY updatedAt DESC")
    fun observe(): Flow<List<NoteRow>>

    @Upsert
    suspend fun save(note: NoteRow)
}

@Database(entities = [NoteRow::class], version = 1, exportSchema = true)
abstract class NotesDb : RoomDatabase() {
    abstract fun notes(): NotesDao
}

Common mistakes

  • Calling allowMainThreadQueries() in production.
  • Building the database more than once; it should be a singleton.
  • Not exporting schemas, so migrations can't be tested.

Senior take & follow-ups

Mention compile-time SQL checks and Flow invalidation; those are Room's real advantages.

Follow-up questions

Think of your answer first, then open the hint.

  1. How many instances of the database should exist?

    Show hint

    One per process, usually provided as a DI singleton.

  2. Does a suspend DAO function need withContext(IO)?

    Show hint

    No, Room handles the threading.

Similar on iOS: The Core Data stack Core Data and SwiftData play Room's role, with an object graph instead of SQL.

Suggest a fix if something here is wrong or out of date.