Android · 16 min read

Senior Android interview questions and what follow-ups test

Twelve senior Android interview questions with what each scores, a weak and a senior answer, and the follow-up: coroutines, Flow, Compose, process death.

A senior Android loop is not a harder quiz. It is a different kind of conversation. The interviewer asks a question a mid-level engineer can answer, listens to the answer, and then asks the one that only someone who has shipped and debugged the thing can answer. Most rejections I have written for Android candidates who were technically strong came from that second question.

This guide covers twelve questions that come up in senior Android loops, grouped by round. For each: what is scored, a weak answer, a senior answer, and the follow-up. The questions are representative of real loops; none is attributed to a company.

What the interviewer writes down

Four columns, the same for every question below.

Signal What I write down What a miss looks like
Signal The answer contains a production detail only a practitioner would know A textbook definition
Structure The reasoning is easy to follow Three half-answers
Depth It holds at the second and third question “It depends” and nothing after it
Judgement The answer changes when the constraint changes, with a reason The same answer, repeated

Coroutines and Flow

1. “What happens to a coroutine launched in viewModelScope when the user rotates the screen?”

What is scored: whether you know which lifecycle owns which scope.

Weak answer: “It gets cancelled and restarted.”

Senior answer: “Nothing. The ViewModel survives a configuration change, so viewModelScope and everything launched in it keep running. The scope is cancelled when the ViewModel is cleared, which happens when the owner is finished for good: the activity finishes, or the navigation destination is popped. What does restart on rotation is the UI’s collection, which is why the UI collects with repeatOnLifecycle or collectAsStateWithLifecycle rather than launch in onCreate.”

The follow-up: “And on process death?” The ViewModel is gone; the coroutine is gone; only what you saved survives. That leads to question 7.

The second follow-up: “The user left the screen, viewModelScope was cancelled, and the work is still running. Why?” Cancellation is cooperative: a coroutine only notices it when it suspends or checks for it. Three things ignore it: blocking code or a CPU loop that never suspends, a catch (e: Exception) that swallows CancellationException, and work handed to another scope, such as a GlobalScope.launch inside the coroutine. Give all three without being prompted, with the fix for each: ensureActive() or runInterruptible, rethrowing CancellationException, and keeping the work in the caller’s scope. The full exchange is in Kotlin interview questions.

2. “You collect a Flow in the ViewModel’s init. What is wrong with that?”

What is scored: understanding cold flows and sharing.

Senior answer: “Collecting in init starts the work as soon as the ViewModel exists, whether or not anyone is looking, and keeps it running while the app is in the background. The usual fix is to expose a StateFlow built with stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), initial). The upstream runs only while the UI collects, and the five-second window keeps it alive through a rotation so the work does not restart.”

val uiState: StateFlow<ProfileUiState> =
    repository.observeProfile(userId)
        .map { ProfileUiState.Loaded(it) }
        .catch { emit(ProfileUiState.Error) }
        .stateIn(
            viewModelScope,
            SharingStarted.WhileSubscribed(stopTimeoutMillis = 5_000),
            ProfileUiState.Loading,
        )

The follow-up: “Why five seconds?” The honest answer: it is a convention that outlasts a configuration change, not a measured constant. A senior candidate says so rather than inventing a reason.

3. “One child coroutine throws. What happens to its siblings?”

What is scored: structured concurrency, which most candidates know as a phrase and few can apply.

Senior answer: “In a regular scope, a failing child cancels its parent, and the parent cancels the other children. With a SupervisorJob, as in viewModelScope, a child’s failure does not cancel its siblings. For launch, an uncaught exception goes to the CoroutineExceptionHandler, which only takes effect on a root coroutine or a direct child of a supervisor. For async, the exception is held until someone calls await. Most bugs I have seen come from assuming a try/catch around launch catches what happens inside it. It does not.”

The follow-up: “You need three requests in parallel, and the screen is useless if any fails.” Now you want the failure to cancel the others: coroutineScope { } with three async calls. The answer changes with the requirement, which is the judgement signal.

4. “Dispatchers.IO or Dispatchers.Default?”

Senior answer: IO for blocking calls (disk, a blocking network client, a database driver without suspend support), Default for CPU work like parsing or image processing. Then the practical point: suspend functions from Room, Retrofit or Ktor already move off the main thread, so wrapping them in withContext(Dispatchers.IO) does nothing useful. The person who says this has read the library code or at least the documentation.

Jetpack Compose

5. “This composable recomposes on every keystroke. Why?”

What is scored: whether you diagnose with tools rather than guess.

Senior answer: “First, measure: Layout Inspector shows recomposition counts per composable. Common causes are reading state too high, so the whole screen reads the text field’s value; passing a lambda or object that is recreated each time; and parameters the compiler cannot treat as stable. The fix depends on which one: read state in the smallest composable that needs it, defer reads with lambdas (for example Modifier.offset { } instead of Modifier.offset(x)), and remember objects that should not be recreated.”

The follow-up: “What changed with strong skipping mode?” With strong skipping, the default in recent Compose compiler versions, composables with unstable parameters can still be skipped, compared by instance equality. So stability matters less than it did, and a list recreated on each recomposition still defeats skipping. A candidate who knows the version context scores higher than one who recites older stability rules as current. The rest of the Compose round (side effects, lists, interop, proving a recomposition) is in Jetpack Compose interview questions.

6. “Where does state live in your Compose screen?”

Senior answer: screen state in the ViewModel as a single StateFlow of a UI state type; UI element state (scroll position, whether a dropdown is open) in the composable with remember or rememberSaveable; state hoisted to the lowest common owner of the composables that read it. Then the reason: a single state object per screen is easy to test and to restore; element state in the ViewModel is noise.

Lifecycle and process death

7. “The user fills in a long form, backgrounds the app, and the process dies. What do they see when they return?”

What is scored: whether you have tested this, which is rarer than it should be.

Weak answer: “The ViewModel keeps the state.”

Senior answer: “The ViewModel does not survive process death. When the user returns, the system recreates the activity and the navigation back stack, but not the ViewModel’s memory. Form input survives only if I saved it: SavedStateHandle in the ViewModel for small values, rememberSaveable for UI element state, and a database for anything large or important, because the saved state bundle is small and a large one can crash with TransactionTooLargeException. I test it with ‘Don’t keep activities’ in developer options, or by killing the process from Android Studio while the app is in the background.”

The follow-up: “The form has a photo the user picked.” A URI in saved state is fine; the bitmap is not. And the URI’s read grant from the photo picker lasts only until the app stops or the device restarts, so after process death the saved URI can fail to open unless you called takePersistableUriPermission() when the user picked it. That second detail is a strong senior signal.

8. “How do you run an upload that must finish even if the user leaves the app?”

Senior answer: WorkManager with a network constraint, input data carrying an id rather than the file itself, and the file already persisted to app storage. For a long upload, a foreground service type (dataSync or a more specific type) is the right tool where the user expects to see progress, and the answer notes that recent Android versions restrict foreground services started from the background and require a declared type.

Architecture

9. “Walk me through the layers of an app you built.”

What is scored: reasons, not diagrams.

Senior answer: UI layer (composables and ViewModels), an optional domain layer for logic shared between ViewModels, a data layer of repositories that own the source of truth (usually Room) and expose Flows. Then the part that scores: why the domain layer exists in this app or does not, which repository owns which data, and what you would change now. More in Android architecture interview questions.

10. “How would you modularise this app for several teams?”

Senior answer: feature modules with an api and impl split so features depend on interfaces, core modules for shared infrastructure, dependency injection wiring the implementations at the app module, and build time measured before and after. At Staff level: who owns core modules, how changes to them are reviewed, and a convention plugin in Gradle so the fifty module build files do not drift.

Performance and delivery

11. “The app starts slowly on low-end devices. What do you do?”

Senior answer: measure cold start with Macrobenchmark on a real low-end device, look at what runs before the first frame (content providers from libraries, dependency graph construction, synchronous disk reads), move or defer it, and add a Baseline Profile so the critical path is compiled ahead of time. Then check R8 is enabled for release. Each step measured, not assumed.

12. “A release is crashing for 3% of users on one Android version. The first hour?”

Senior answer: halt the staged rollout in Play Console, turn off the feature flag if there is one, read the crash cluster for the common device and OS pattern, then choose between a server-side mitigation, a flag, and a hotfix build. Then the durable fix: the test or pre-launch check that would have caught it. Starting with “I would open the code” misses what the question is about.

The follow-ups, as a pattern

Follow-up type Android example What a strong answer does
Change the constraint “Now minSdk is 24.” Changes the answer and says why
Break it “The process dies mid-upload.” Names the failure and the recovery
Scale it “Six teams share this module.” Moves from code to ownership
Measure it “How do you know startup improved?” Names the tool and the number
Cost it “What does Hilt cost you here?” Names a real cost: build time, learning curve

What changed in 2026 loops

Some Android answers that were current two years ago now sound dated. These are the toolchain changes a 2026 interviewer can assume you know. They are facts about Kotlin and Compose, not claims about what any company asks.

  • K2 is the compiler. Since Kotlin 2.0.0 the K2 compiler is the default and stable on every target. Build-speed and compiler-plugin answers that describe the old frontend describe a toolchain most projects have left.
  • The Compose compiler ships with Kotlin. From Kotlin 2.0 the Compose compiler lives in the Kotlin repository and is applied with its own Gradle plugin, so the Compose compiler version follows the Kotlin version. “Which Compose compiler are you on?” now has a one-line answer.
  • Strong skipping is on by default. Since Kotlin 2.0.20, composables with unstable parameters can still be skipped, compared by instance equality, and lambdas are remembered automatically. Stability still matters; an answer that treats every unstable parameter as a guaranteed recomposition is out of date.
  • Compose is the default UI toolkit. Android’s own documentation recommends Compose for new UI, so interop with Views is the question for existing apps, not the choice for new ones.

If an answer of yours predates these, say which version you are describing. Version context is part of the answer.

How to use this list

Say each answer out loud before reading the senior version, then ask yourself the follow-up. If your second answer is weaker than your first, that is the one to work on. The design round is covered in Android system design interview.

Sources

Free guide: the Phoenix Method