Room DAO: Flow<List<User>> vs suspend fun getUsers(): List<User>

This tests reactivity versus one-shot queries in Room and coroutines. Flow emits on every table change on Room's dispatcher, while suspend returns a single snapshot requiring manual refresh.
WHAT THIS TESTS: The interviewer wants to know if you understand the architectural contract between Room, Kotlin coroutines, and the UI layer. Specifically, they are looking for clarity on reactive data observation versus one-shot querying, dispatcher behavior, and lifecycle-aware collection. This distinction is central to building responsive apps that do not waste resources polling the database or miss updates.
A GOOD ANSWER COVERS: First, explain that Flow<List<User>> is an observable query. Room sets up an internal observer on the table; whenever the data changes, Room re-runs the query and emits the new list downstream. This happens automatically without manual refresh calls. Second, explain that suspend fun getUsers(): List<User> is a one-shot operation. It runs the query once, returns the result, and completes. The caller receives a static snapshot that becomes stale immediately if another process writes to the database. Third, discuss threading. Room executes both variants on a background dispatcher that it manages for queries, so neither blocks the main thread. However, the Flow remains active after the first emission, holding resources until the collector cancels it. Fourth, cover UI implications. A Flow should be collected in a lifecycle-aware scope such as viewModelScope or lifecycleScope so the UI re-composes or updates automatically. A suspend function result must be stored in state and manually refreshed, often through a user pull-to-gesture or a periodic polling mechanism. Fifth, mention error handling and backpressure. Flow allows you to handle errors and apply operators like distinctUntilChanged or map in the ViewModel, while a suspend function pushes error handling to the single call site.
COMMON WRONG ANSWERS: A major red flag is saying Flow is just an asynchronous wrapper around a suspend function. Another is claiming that suspend fun automatically re-runs when the database changes. Some candidates incorrectly state that Flow queries run on the main thread unless you specify a dispatcher, which reveals confusion about Room's internal threading model. Also, suggesting that you should call a suspend function in a loop to mimic Flow shows a lack of understanding of reactive streams and battery efficiency.
LIKELY FOLLOW-UPS: The interviewer may ask how you would handle a rapidly changing table with Flow to avoid excessive UI updates; expect to mention distinctUntilChanged or conflate. They might ask what happens if you collect a DAO Flow in a Fragment without lifecycle awareness; you should mention repeatOnLifecycle to prevent wasted background work. Another follow-up is how to combine this Flow with network fetching; a good answer introduces combine or flatMapLatest in the ViewModel. They may also probe error handling, asking what happens if the database is locked or corrupted during emission.
ONE CONCRETE EXAMPLE: Imagine a chat app displaying a user list. Using Flow<List<User>>, the screen updates instantly when a new user is inserted by a background sync worker. The ViewModel collects the Flow in viewModelScope and exposes it to Compose via StateFlow; the UI re-composes automatically. If you instead used suspend fun getUsers(), the background worker would insert the new user, but the screen would remain stale until the user triggered a manual refresh or the activity recreated and called the function again.
Source: developer.android.com
Read the original → developer.android.com
- #room
- #coroutines
- #flow
- #android-architecture
- #dao
Get five bites like this every day.
Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.