cswork / libraryOpen practice workspace

Articles

Android Interview Questions for Senior Engineers: Kotlin, Coroutines, Compose, and Architecture

Prepare for senior Android interviews with practical Kotlin, Coroutines, Compose, architecture, and performance questions plus a focused study plan.

A senior Android interview can expose shallow knowledge with one innocent question: “Does a suspend function run on a background thread?” A confident “yes” sounds plausible, but it is wrong. Suspension and thread selection are separate concerns.

That distinction captures what senior Android interviews actually measure. Interviewers are less interested in whether you can recite APIs than whether you can connect Kotlin semantics, coroutine lifetimes, Compose state, process death, offline behavior, and measurable performance into one reliable product.

Then use this guide to sharpen the Android-specific reasoning behind your answers.

Quick Answer: What Senior Android Interviews Test

A strong candidate does more than name Jetpack libraries. They define the requirement, choose an ownership boundary, explain failure behavior, and describe how they would verify the result.

AreaStrong senior signalWeak signal
KotlinExplains nullability, immutability, variance, equality, and API design in contextRecites syntax without discussing contracts
CoroutinesChooses scopes and dispatchers by lifetime, cost, and failure policyUses GlobalScope or assumes suspend means background
ComposeModels state ownership, side effects, stability, and measured performanceCalls every recomposition a performance bug
ArchitectureDefines sources of truth, offline behavior, module boundaries, and trade-offsDraws layers without data flow or failure paths
LeadershipConnects technical choices to product risk, team velocity, and evidenceDescribes only personal implementation work

Kotlin and Coroutines Interview Questions

1. Does suspend move work off the main thread?

No. A suspending function can pause without blocking its thread, but it continues in the coroutine's context unless the implementation changes context. CPU-heavy work belongs on Dispatchers.Default; blocking I/O usually belongs on Dispatchers.IO.

The stronger design principle is main safety. As Android's coroutine guidance explains, a repository that performs blocking work should move that work internally, so callers can invoke it safely from the main thread without knowing implementation details.

2. How do you choose the right coroutine scope?

Start with the work's required lifetime. Work needed only while a screen model exists fits viewModelScope. Work tied to a composable should use lifecycle-aware Compose APIs rather than a manually created global scope.

If a queued upload must finish after navigation, app backgrounding, or process death, a ViewModel coroutine is insufficient. Persist the intent and schedule durable execution with WorkManager when its guarantees fit the requirement.

3. How should cancellation and cleanup work?

Coroutine cancellation is cooperative. Suspending calls usually check it automatically, but a long CPU loop must yield or check isActive. Cleanup belongs in finally; non-cancellable cleanup should be small and reserved for work that truly must finish.

Do not swallow CancellationException inside a broad catch. Rethrow it so the parent-child cancellation contract remains intact.

4. When would you use coroutineScope versus supervisorScope?

Use coroutineScope when sibling tasks form one operation and any failure should cancel the whole unit. Use supervisorScope when independent children can fail separately and the parent has an explicit partial-failure policy.

A senior answer names that policy. “Use supervisor everywhere so crashes do not propagate” merely hides failures and can leave the UI showing inconsistent data.

5. StateFlow, SharedFlow, or a cold Flow?

Use a cold Flow when each collector should start the upstream work. Use StateFlow for observable state with a current value. Use SharedFlow for shared emissions when replay and buffering behavior are deliberately configured.

For UI architecture, prefer a durable state model over fragile “one-time event” tricks. Decide what should happen after rotation, navigation, or a late collector, then choose the primitive.

6. Review this ViewModel: what would you ask next?

class OrdersViewModel(
    repository: OrdersRepository
) : ViewModel() {
    val orders: StateFlow<List<Order>> = repository.observeOrders()
        .stateIn(
            scope = viewModelScope,
            started = SharingStarted.WhileSubscribed(5_000),
            initialValue = emptyList()
        )

    private var refreshJob: Job? = null

    fun refresh() {
        refreshJob?.cancel()
        refreshJob = viewModelScope.launch {
            try {
                repository.refreshOrders() // Repository keeps this main-safe.
            } catch (cancelled: CancellationException) {
                throw cancelled
            } catch (failure: Throwable) {
                // Publish a recoverable UI error.
            }
        }
    }
}

A good review asks whether refreshes should cancel or coalesce, how errors enter UI state, whether the five-second sharing timeout matches navigation behavior, and how tests control dispatchers. It also confirms that the repository owns threading and that durable writes do not depend on the ViewModel surviving.

Jetpack Compose Interview Questions

7. Where should Compose state live?

Keep local UI state near the composable when no other component needs it. Hoist state to a state holder or ViewModel when business logic, persistence, navigation, or multiple UI elements need coordinated access.

The key is a clear source of truth: immutable state flows down, events flow up. “Everything belongs in a ViewModel” is as crude as keeping all state inside composables.

8. How do you diagnose excessive recomposition?

First separate recomposition from visible jank. Recomposition can be cheap; the actual bottleneck may be composition, layout, drawing, image decoding, allocation, or main-thread I/O.

Inspect state reads and object stability, use stable keys in lazy lists, defer rapidly changing reads when possible, and use derivedStateOf only when derived output changes less often than its inputs. Treat stability annotations as contracts, not decoration.

9. How should side effects and lifecycle-aware collection work?

A composable should describe UI, not start unmanaged work during recomposition. Use LaunchedEffect for suspending work keyed to composition inputs, DisposableEffect for setup with cleanup, and lifecycle-aware collection for Android Flow state.

Explain what restarts the effect, what cancels it, and whether replay after recreation is correct. Those three questions usually matter more than remembering an API name.

Android Architecture Interview Questions

10. Design an offline-first order-management app

Begin with product semantics. Can users create orders offline? Must stock be reserved immediately? Which conflicts can merge, and which require human resolution?

A practical design follows Android's offline-first guidance and uses Room as the local source of truth. The UI observes local data through a repository; network responses update the database rather than bypassing it. Pending mutations carry stable IDs, timestamps or versions, retry state, and idempotency information.

11. When should the app use WorkManager?

Use WorkManager for appropriate persistent work that should survive app restarts and respect constraints such as connectivity. Examples include syncing queued orders or uploading diagnostic bundles.

It is not a replacement for every coroutine. A screen refresh that no longer matters after navigation should remain screen-scoped; an immediate user-visible operation may need a different execution model.

12. How would you modularize a growing Android codebase?

Optimize for high cohesion and controlled dependencies, not the largest possible module count. Feature modules can own UI and coordination; data modules can own repositories and data sources; shared modules should represent genuinely stable capabilities.

Then discuss costs: build graph complexity, public API maintenance, navigation contracts, test fixtures, and cross-feature changes. There is no universally correct module diagram.

A Senior-Level Architecture Answer, Step by Step

When the prompt is ambiguous, use this sequence:

  1. Clarify behavior: supported devices, offline expectations, freshness, security, accessibility, and failure UX.
  2. Define state ownership: identify the durable source of truth and the temporary UI state.
  3. Draw data flow: events enter the ViewModel, repositories coordinate sources, persisted updates drive UI state.
  4. Choose lifetimes: assign screen work, app-session work, and persistent work to different owners.
  5. Handle failure: cover retries, duplicate requests, cancellation, conflict resolution, and process death.
  6. Verify: name unit, integration, UI, benchmark, and observability checks tied to the riskiest assumptions.

This structure also works for company-specific interview prep: swap in the target company's product constraints and likely scale, then practice defending the trade-offs.

Performance and Testing Questions

13. A LazyColumn scrolls poorly. What do you do?

Reproduce the problem in a release-like build and measure frames before changing code. Check stable keys, item identity, state reads, heavy composition, image work, allocations, and whether expensive operations occur on the main thread.

Use traces and Compose performance tooling to locate the phase causing delay. Make one targeted change, then repeat the same benchmark so the result is evidence rather than intuition.

14. How do you improve startup without guessing?

Define cold, warm, and hot startup, then measure the scenario that matters. Look for eager dependency initialization, disk reads, provider startup, class loading, and first-frame work.

Baseline Profiles can precompile critical user journeys, while Macrobenchmark can measure startup and frame timing from outside the app process. Neither replaces finding unnecessary work.

15. What should the test strategy cover?

Test reducers and domain logic as ordinary Kotlin, repositories against controlled data sources, coroutine behavior with test dispatchers, database/network integration, and critical Compose journeys. Add benchmarks for startup or scrolling only where performance is a product requirement.

A senior candidate also discusses test ownership: which failures should be caught before review, in CI, in staged rollout, or through production telemetry.

How Interviewers Score Senior Android Answers

DimensionStrong evidenceCommon miss
CorrectnessAccurate platform semantics and explicit assumptionsFramework folklore stated as fact
Trade-offsExplains why the choice fits this lifetime and productNames a “best practice” with no context
ReliabilityCovers cancellation, retries, process death, and conflictsOnly describes the happy path
VerificationDefines tests, traces, benchmarks, and success criteriaSays “optimize” without measurement
CommunicationMoves from requirements to decision to evidenceJumps between APIs without a model

A Focused 5-Day Android Interview Plan

DayFocusPractice output
1Kotlin and coroutine semanticsExplain five scope, dispatcher, cancellation, and failure scenarios aloud
2Flow and Compose stateReview one state holder and diagnose one recomposition trace
3Offline-first architectureDesign the order app, including retries and process death
4Performance and testingBuild a measurement plan for startup and list scrolling
5Full-loop rehearsalRun a timed coding, architecture, and leadership mock

Pair technical practice with behavioral and leadership preparation, because senior loops score ownership and influence alongside implementation.

Frequently Asked Questions

Are senior Android interviews mostly LeetCode?

It depends on the company. Many loops still include data structures and algorithms, but senior Android roles commonly add Kotlin, concurrency, architecture, debugging, performance, product judgment, and leadership. Prepare from the actual loop rather than assuming mobile expertise replaces general coding.

Should I memorize every Jetpack Compose API?

No. Know the core model: state ownership, recomposition, effects, lifecycle, stability, lazy-list identity, and testing. Interviewers can forgive a forgotten function name more easily than an effect that restarts incorrectly or state with no clear owner.

Is Clean Architecture required?

No single architecture is universally required. Show clear boundaries, unidirectional data flow where it helps, testable dependencies, a defined source of truth, and costs appropriate to the product and team. Extra layers without a concrete responsibility are not a senior signal.

How should I answer a question I have not seen before?

Clarify the requirement, state assumptions, identify the hardest lifecycle or failure constraint, offer a simple baseline, and improve it deliberately. Interviewers are watching your decision process. A transparent correction is stronger than defending an early guess.

Practice the Decisions, Not Just the APIs

The best senior Android answers connect four layers: Kotlin semantics, lifecycle-aware concurrency, observable UI state, and product-level reliability. They also include a plan to measure whether the design works.

Your goal is not to sound like documentation. It is to make sound engineering decisions under pressure and explain them clearly.

Official Sources