Mobile System Design Quiz
ADVANCED › System Design
For an offline-first feature, where should the single source of truth live?
- In the ViewModel's StateFlow
- In an in-memory cache inside the repository
- In a local persistent database such as Room
- On the backend, re-fetched every time the screen opens
Answer: In a local persistent database such as Room
Offline-first keeps a local persistent database (Room) as the SSOT so the app works without network; the UI observes the database, and network responses update it.
What does the stale-while-revalidate (SWR) strategy do?
- Blocks the UI until fresh network data fully arrives, then renders it
- Shows cached data instantly, then refreshes from the network in the background
- Reads only from the local cache and never contacts the network at all
- Fetches from the network first, using the cache only when that fails
Answer: Shows cached data instantly, then refreshes from the network in the background
SWR serves stale cached data instantly for responsiveness while revalidating in the background; option 4 describes network-first and option 3 describes cache-only.
For an infinite, frequently-changing feed, which pagination approach avoids page drift and hides database internals from the client?
- Offset/limit pagination
- Page-number pagination
- Loading the entire list in one request
- Cursor (token-based) pagination
Answer: Cursor (token-based) pagination
Cursor pagination passes an opaque token, so it is resilient to insertions/deletions (no page drift) and decouples the client from the underlying ordering field, unlike offset/page-number.
In unidirectional data flow (UDF), how do state and events move?
- State flows down to the UI; events flow up to the single source of truth
- Both state and events flow from the UI downward into the data layer
- Events flow down to the UI while state flows up to the ViewModel
- The UI mutates the underlying data source directly, without any events
Answer: State flows down to the UI; events flow up to the single source of truth
Under UDF, application state flows from the SSOT down to the UI while user events flow up to the SSOT where the data is actually mutated.
What is the modern, recommended way to keep a music player running while the app is backgrounded?
- A plain Service using MediaPlayer and showing no notification at all
- Playing the audio directly on the UI thread from within the Activity
- A Media3 MediaSessionService run as a foreground service with a notification
- A WorkManager periodic background worker that streams audio chunks
Answer: A Media3 MediaSessionService run as a foreground service with a notification
androidx.media3's MediaSessionService running as a foreground service with a media-style notification is the current approach; WorkManager is for deferrable work, and a service without a foreground notification gets killed.
What is the best way to represent loading, success, empty, and error states for a screen?
- Throw exceptions and catch them directly inside the Composable
- A sealed UI state in a StateFlow from the ViewModel
- Use separate flags like isLoading, isError, and isEmpty
- Make a blocking synchronous call and show a spinner dialog
Answer: A sealed UI state in a StateFlow from the ViewModel
A sealed UI state makes the states mutually exclusive and exhaustively handled and is exposed immutably via StateFlow under UDF; independent booleans can drift into inconsistent combinations.
What does Android's guidance recommend for test doubles in the data layer?
- Prefer fakes over mocks for repositories and data sources
- Use Mockito mocks for all tests, even for value classes
- Rely only on instrumented end-to-end tests, not unit tests
- Test private methods directly by reaching in with reflection
Answer: Prefer fakes over mocks for repositories and data sources
Android recommends fakes over mocks because fakes behave like real implementations, producing less brittle, more realistic tests of repositories and data sources.
At the start of a mobile system design interview, what should you do before sketching any architecture?
- Start writing Compose UI code immediately to show visible progress
- Clarify functional and non-functional requirements, then set scope
- Commit to a Room schema early, before discussing the problem at all
- Pick a third-party image-loading library before defining scope
Answer: Clarify functional and non-functional requirements, then set scope
Interviewers judge your process, so you first gather functional and non-functional requirements and scope the problem; jumping into code or premature implementation details signals weak trade-off reasoning.
In a Paging 3 setup with offline support, what is the role of a RemoteMediator?
- It renders the paged list directly inside the target Composable
- It encrypts the pagination cursors for network transport security
- It replaces Room entirely with a volatile in-memory-only page cache
- It fetches network pages into Room, which the PagingSource reads as the SSOT
Answer: It fetches network pages into Room, which the PagingSource reads as the SSOT
RemoteMediator loads pages from the network and persists them into Room; the PagingSource reads from Room, so the database drives the list and enables offline scrolling.
A list endpoint begins returning HTTP 429. What is the appropriate client behavior?
- Retry with exponential backoff and jitter, and honor Retry-After
- Retry immediately in a tight loop until the request finally succeeds
- Show a fatal error and force the user to reinstall the app first
- Permanently disable the feature and never call the API again from now on
Answer: Retry with exponential backoff and jitter, and honor Retry-After
429 means rate-limited, so you back off exponentially (ideally with jitter) and respect Retry-After; tight-loop retries worsen the throttling and a permanent shutoff is an overreaction.
What is the main reason to add the optional Domain layer of use cases in Android's recommended architecture?
- To let Composables call the network directly without a repository
- To store the app's single source of truth in place of the Room database
- To hold reusable or complex business logic and cut ViewModel duplication
- To replace repositories as the single data source across the app
Answer: To hold reusable or complex business logic and cut ViewModel duplication
Use cases extract reusable or complex business logic so multiple ViewModels can share it and stay lean; the layer is optional and does not replace repositories or the SSOT.
For periodic, connectivity-aware background sync of an offline news reader, which API is the right fit?
- A foreground service that runs nonstop in the background for syncing
- A while(true) loop polling the network on the main thread forever
- AlarmManager firing every second to fetch new articles in a loop
- WorkManager with a network constraint for guaranteed periodic sync
Answer: WorkManager with a network constraint for guaranteed periodic sync
WorkManager is built for deferrable, guaranteed background work with constraints like requiresNetwork; a perpetual foreground service, main-thread loop, or per-second alarm waste battery and are the wrong tools.
Why should a ViewModel expose its UI state as an immutable StateFlow rather than a public MutableStateFlow?
- A read-only StateFlow is measurably faster than a MutableStateFlow in Compose
- It keeps the ViewModel as the sole writer, so the UI reads state and sends events
- Compose cannot collect from a MutableStateFlow, so only immutable flows work there
- It lets the UI change state directly, so no events need to be sent upward
Answer: It keeps the ViewModel as the sole writer, so the UI reads state and sends events
Exposing state as a read-only StateFlow keeps the ViewModel the only writer, upholding unidirectional data flow where the UI reads state and sends events upward; it is about encapsulation, not raw speed.
For a real-time chat feature, which approach best supports low-latency bidirectional delivery while staying offline-capable?
- A persistent WebSocket for live messages, with Room as the SSOT
- Polling a REST endpoint every 60 seconds for new messages and updates
- A single REST GET when the chat screen first opens, then no more sync
- Re-downloading the entire conversation on every keystroke from the server
Answer: A persistent WebSocket for live messages, with Room as the SSOT
A persistent WebSocket gives low-latency two-way delivery, and persisting messages into Room as the SSOT keeps history available offline and lets the UI observe a single consistent source; polling and one-off fetches add latency or miss updates.