Skip to content
All writing

Engineering Interviews

Senior Android interview questions: the 5 follow-ups that decide the loop

Your first answer rarely decides a senior Android interview. The follow-up does. Five follow-ups, the answer that loses the point, the one that earns it, and what to say when you wobble.

6 min read

The candidate knew coroutines. viewModelScope, Dispatchers.IO, withContext, all in the right places. Then I asked what happens when the user leaves the screen halfway through the request. The answer was "it gets cancelled", and the next question took it apart.

After 15 years in mobile and 500+ technical interviews from the hiring side, the pattern holds on Android exactly as it does on iOS: the correct first answer is the entry fee. The score is decided by the follow-up.

On Android the follow-up almost always asks the same thing in a different costume: who owns this work, and what outlives what?

  • Senior Android loops are graded on the second answer, not the first.
  • Most losing answers name the right API and miss the lifetime behind it.
  • The skill being tested: knowing when your own answer stops being true.

The 5 follow-ups at a glance

TopicFirst questionThe follow-up that decides it
CoroutinesHow do you load data off the main thread?Does cancellation still work in your error handling?
Compose stateWhere does this field's state live?Why does it clear after rotation?
FlowHow do you collect a Flow in the UI?Is it still collecting in the background?
MemoryHow do you avoid leaks?Can your ViewModel hold a Context?
Offline syncDesign offline edits.The app is killed before the network returns. Does it sync?

1. Coroutines: does cancellation still work?

The first question: "How do you load this screen's data off the main thread?" Most candidates answer well: viewModelScope.launch, the repository switches to Dispatchers.IO, and the scope cancels the work when the ViewModel is cleared. Then I show them their own error handling.

Kotlin
viewModelScope.launch {
    val result = runCatching { repository.load() }   // catches CancellationException too
    _state.value = result.fold(::Loaded, ::Failed)
}

The follow-up: "The user leaves the screen during the request. What happens?"

The answer that loses the point: "viewModelScope cancels it, so nothing."

The answer that earns it: "The scope requests cancellation, but cancellation is cooperative: it arrives as a CancellationException at the next suspension point. runCatching catches every Throwable, including that one, so this code turns cancellation into a failure and keeps going. I'd catch only the exceptions I expect, or rethrow CancellationException."

Kotlin
viewModelScope.launch {
    try {
        _state.value = Loaded(repository.load())
    } catch (e: IOException) {
        _state.value = Failed(e)
    }   // CancellationException passes through
}

What gets scored: the mid-level answer knows the scope cancels. The senior answer knows how cancellation is delivered, and what swallows it.

Recovery line: "Let me correct that. The scope asks for cancellation, but my runCatching catches it. I'd narrow the catch so cancellation propagates."

2. Compose state: why does it clear after rotation?

The first question: "Where does the state of this text field live?"

The follow-up: "Users type a long message, rotate the phone, and the field is empty. Why?"

Kotlin
@Composable
fun MessageField() {
    var text by remember { mutableStateOf("") }   // survives recomposition only
    TextField(value = text, onValueChange = { text = it })
}

The answer that loses the point: "Compose recomposes on rotation, so the state resets. I'd store it somewhere."

The answer that earns it: "Rotation recreates the Activity, and remember only survives recomposition, not recreation. For small UI state I'd use rememberSaveable, which goes through the saved instance state and so also survives process death. For state the screen depends on, I'd hoist it to the ViewModel, which survives rotation, and use SavedStateHandle for the part that must survive process death."

Kotlin
var text by rememberSaveable { mutableStateOf("") }   // survives rotation and process death

What gets scored: not the function name. It's that you can name three different deaths, recomposition, recreation and process death, and say which state survives which.

Recovery line: "My first answer blurred two things. Recomposition keeps remember; recreation doesn't. Let me pick the holder by which death the state has to survive."

3. Flow: is it still collecting in the background?

The first question: "How do you collect this Flow in a Fragment?"

Kotlin
lifecycleScope.launch {
    viewModel.updates.collect { render(it) }   // keeps collecting while the app is in the background
}

The follow-up: "The user switches to another app for ten minutes. What is this code doing?"

The answer that loses the point: "Nothing, the screen isn't visible."

The answer that earns it: "Still collecting. lifecycleScope is cancelled only when the lifecycle is destroyed, so the upstream keeps producing and render keeps running with no one looking. I'd collect inside repeatOnLifecycle(Lifecycle.State.STARTED), which stops when the screen stops and restarts when it comes back. In Compose, collectAsStateWithLifecycle does the same."

Kotlin
viewLifecycleOwner.lifecycleScope.launch {
    viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
        viewModel.updates.collect { render(it) }
    }
}

What gets scored: "not visible" is not "not running". Seniors tie every collector to a lifecycle on purpose.

Recovery line: "I assumed the lifecycle stops it. It only stops at destroy. Let me scope the collection to STARTED."

4. Memory: can your ViewModel hold a Context?

The first question: "How do you avoid memory leaks?" Most candidates mention LeakCanary and not holding Views in long-lived objects.

The follow-up: "This ViewModel needs a Context for string resources. Can it take the Activity?"

The answer that loses the point: "Yes, I pass this from the Activity when I create it."

The answer that earns it: "No. A ViewModel outlives the Activity across rotation, so holding the Activity keeps the destroyed one and its whole view tree in memory. If I need a Context there, it's the application Context, through AndroidViewModel or injection. Better still, the ViewModel exposes state and the UI resolves strings, so the ViewModel doesn't need a Context at all."

What gets scored: the leak isn't the Context. It's a shorter lifetime referenced from a longer one. Seniors check lifetimes, not keywords.

Recovery line: "Let me take that back. The ViewModel outlives the Activity, so it must not hold it. The application Context, or no Context at all."

5. Offline sync: does it survive the process?

The first question: "Design offline edits for a notes app." Strong candidates say Room as the source of truth and a sync when the network returns.

The follow-up: "The user edits offline, then the system kills the app. An hour later the network comes back. Does the edit reach the server?"

The answer that loses the point: "Yes, the repository listens for connectivity and retries."

The answer that earns it: "Not if the retry lives in memory. The process is gone, and so is any coroutine or listener that was waiting. I'd write the edit to Room with a pending state, an outbox, and enqueue unique work with WorkManager and a network constraint, so the system runs the sync after process death or a reboot. Because it may run more than once, the request carries an id the client generated, so the server can ignore a duplicate."

What gets scored: senior Android system design starts from the fact that your process is temporary. Anything that must happen later has to be written down and scheduled with the system.

Recovery line: "My retry dies with the process. Let me persist the edit and let WorkManager own the sync."

The pattern behind all five

Read the losing answers together: "the scope cancels it", "store it somewhere", "not visible, so not running", "pass this", "the repository retries".

Each one names something real and skips the lifetime behind it. On Android the follow-up is almost always the same question: who owns this, and what outlives what? Ask it before the interviewer does.

A 10-minute drill before your next loop

  • Pick one topic from your last interview.
  • Say your first answer out loud, on a timer, in under 30 seconds.
  • Ask yourself: "What happens on rotation, in the background and after process death?"
  • Answer out loud. Write down the one case you reached for and didn't have.
  • Change one fact (the screen goes away, the network drops, the app is killed) and answer again.

Reading answers feels like progress. Saying them under a changed constraint is the practice that shows up in the room. The iOS version of this list is the 5 follow-ups that decide a senior iOS loop.

Questions engineers ask about senior Android interviews

What do interviewers look for in a senior Android interview?

Ownership and lifetime. A correct API name gets you to the next question. What separates senior from mid-level is knowing who owns the work, what survives rotation and process death, and what your choice costs.

How do I prepare for Android follow-up questions?

Practise out loud, not by reading. For every answer, write the follow-up that breaks it: the screen goes away, the process dies, the network drops. Then answer again with that fact changed.

How long does it take to prepare for a senior Android loop?

With weeks, cover the full loop: Kotlin, coroutines and Flow, Compose, the Android runtime, architecture, offline data and system design. With days, focus on the highest-frequency questions and rehearse the follow-ups out loud.

Share this essay