Skip to content

Android Developer interview questions

100 real questions with model answers and explanations for Middle candidates.

See a Android Developer resume example

Practice with flashcards

Spaced repetition · Hunter Pass

Questions

concurrencycoroutineskotlin

Structured concurrency ties coroutine lifetimes to an explicit scope and its job hierarchy.

  • A parent does not complete until all of its child coroutines complete.
  • Cancelling a scope propagates cancellation to its children, so work does not outlive its owner.
  • An unhandled child failure normally cancels the parent and its sibling coroutines.
  • Builders such as coroutineScope preserve this structure without blocking the current thread.

Why interviewers ask this: The interviewer is checking whether the candidate understands coroutine lifetime, ownership, and failure propagation rather than only coroutine syntax.

coroutines

A CoroutineScope provides the CoroutineContext used to launch coroutines, while Job represents their lifecycle.

  • CoroutineContext is a set of elements such as Job, dispatcher, name, and exception handler.
  • A builder inherits its scope's context and adds or replaces elements supplied to that builder.
  • The new coroutine usually creates a child Job linked to the Job in the inherited context.
  • Cancelling the scope's Job cancels the coroutine tree rooted in that scope.

Why interviewers ask this: The interviewer is evaluating whether the candidate can reason about how coroutine execution settings and lifecycle ownership are composed.

Use supervision when sibling coroutines should fail independently while remaining owned by the same parent.

  • With a regular Job, an unhandled child failure cancels the parent and its other children.
  • With SupervisorJob, one child's failure does not automatically cancel its siblings or supervisor.
  • Each supervised child must expose or handle its own failure because the supervisor does not absorb it as success.
  • supervisorScope gives the same failure isolation for a bounded suspending block.

Why interviewers ask this: The interviewer is checking whether the candidate can choose failure propagation semantics deliberately instead of treating supervision as generic error handling.

coroutines

Choose a dispatcher according to the kind of work rather than creating threads directly.

  • Dispatchers.Main runs UI work on Android's main thread and Main.immediate can run inline when already there.
  • Dispatchers.IO is designed for blocking I/O and can expand beyond the CPU-oriented pool.
  • Dispatchers.Default is sized for CPU-bound work such as parsing or calculations.
  • A well-designed suspending function uses withContext internally when it must move blocking or CPU-heavy work.

Why interviewers ask this: The interviewer is evaluating whether the candidate understands dispatcher intent and main-safety at API boundaries.

resiliencecoroutines

Coroutine cancellation is cooperative because a coroutine stops only when it observes that its Job is cancelled.

  • Standard suspending functions check cancellation and throw CancellationException.
  • CPU loops should call ensureActive or yield periodically when they have no suspension points.
  • CancellationException should normally be rethrown rather than converted into a domain failure.
  • Cleanup that must suspend can run in finally with NonCancellable used only for that bounded cleanup.

Why interviewers ask this: The interviewer is checking whether the candidate can write cancellable coroutine code without swallowing cancellation or leaking work.

asyncerror-handling

launch reports an uncaught exception immediately, while async stores it until the Deferred result is awaited.

  • A root launch can deliver its exception to a CoroutineExceptionHandler after normal propagation rules apply.
  • await rethrows the exception captured by async to the awaiting caller.
  • A failed child still participates in parent cancellation even if its Deferred is never awaited.
  • try and catch should surround the suspending call or await point where recovery is meaningful.

Why interviewers ask this: The interviewer is evaluating whether the candidate understands that coroutine builders change how results are consumed, not the underlying job hierarchy.

coroutines

A suspending function can pause without occupying its current thread, whereas a blocking function holds the thread until it finishes.

  • The suspend modifier enables suspension points but does not automatically move work off the caller's dispatcher.
  • A suspending function may still block if it calls a blocking API directly.
  • Blocking I/O should be isolated with withContext on an appropriate dispatcher.
  • Suspension preserves local control flow through continuations instead of requiring callbacks.

Why interviewers ask this: The interviewer is checking whether the candidate avoids the common assumption that suspend automatically makes any operation asynchronous or main-safe.

android-componentscoroutines

withContext derives a context for a suspending block and resumes the caller in its original context afterward.

  • Supplied elements replace matching keys from the current CoroutineContext, such as the dispatcher.
  • The call suspends until the block completes and returns its result directly.
  • It preserves structured concurrency because the caller waits for the block and its children.
  • Cancellation remains linked to the caller unless the Job is deliberately replaced, which is rarely appropriate.

Why interviewers ask this: The interviewer is evaluating whether the candidate sees withContext as scoped context switching rather than a fire-and-forget coroutine builder.

coroutineskotlin

A regular Flow is cold because its producer block starts separately for each collector.

  • Creating a Flow does not execute its upstream code.
  • Every collection repeats upstream work unless the Flow is shared with stateIn or shareIn.
  • Each collector receives values from its own execution of the flow builder.
  • Collection suspends until the flow completes or the collecting coroutine is cancelled.

Why interviewers ask this: The interviewer is checking whether the candidate understands execution, duplication, and lifecycle implications of cold streams.

coroutines

StateFlow models observable state, while SharedFlow is a configurable hot stream for broadcasts.

  • StateFlow always has a current value and immediately gives the latest value to a new collector.
  • StateFlow suppresses updates that are equal according to equality comparison.
  • SharedFlow configures replay, extra buffer capacity, and overflow behavior without requiring an initial value.
  • Neither stream completes merely because collectors disappear, so its owning scope controls its lifetime.

Why interviewers ask this: The interviewer is evaluating whether the candidate can select a hot stream based on state semantics rather than API familiarity.

ci-cd

flowOn changes the coroutine context of the upstream operators that precede it.

  • Downstream collection continues in the collector's context, preserving context safety.
  • When the dispatcher changes, flowOn introduces a coroutine and channel boundary between upstream and downstream.
  • Multiple flowOn calls apply to their respective upstream segments rather than the whole pipeline globally.
  • emit should not switch context manually inside a flow builder because Flow enforces context preservation.

Why interviewers ask this: The interviewer is checking whether the candidate can place execution boundaries correctly in a Flow pipeline.

Choose the operator according to whether new input should cancel, replace, or run alongside earlier work.

  • mapLatest cancels the current transformation when a newer upstream value arrives.
  • flatMapLatest switches to the newest inner Flow and cancels collection of the previous one.
  • flatMapMerge collects multiple inner Flows concurrently up to its concurrency limit.
  • Regular map is sufficient when each value maps to one non-Flow result and all transformations must finish.

Why interviewers ask this: The interviewer is evaluating whether the candidate understands cancellation and concurrency semantics of common Flow transformations.

concurrency

These operators change how a slow collector interacts with a faster upstream producer.

  • buffer lets upstream production and downstream processing overlap through a channel.
  • conflate keeps only the most recent pending value while the collector is busy.
  • collectLatest cancels the current collector block when a new value arrives.
  • Conflation is appropriate only when intermediate values are replaceable rather than required events.

Why interviewers ask this: The interviewer is checking whether the candidate can distinguish throughput optimization from deliberate loss or cancellation of intermediate work.

lifecycle

Collect UI Flows only while the relevant Lifecycle is in an active state.

  • repeatOnLifecycle launches the collection block at the chosen state and cancels it when the lifecycle falls below that state.
  • A new block is launched when the lifecycle re-enters the state, so the upstream must tolerate repeated collection.
  • Multiple Flows that need parallel collection should be launched as separate children inside the repeat block.
  • In Compose, collectAsStateWithLifecycle provides the corresponding state adapter for lifecycle-aware collection.

Why interviewers ask this: The interviewer is evaluating whether the candidate can align stream collection with UI visibility without retaining obsolete collectors.

callbacks

Use callbackFlow to adapt a callback-based API into a cold Flow with safe concurrent emission.

  • trySend forwards callback values to the Flow's channel without requiring the callback to suspend.
  • awaitClose keeps the producer active and unregisters the callback when collection ends.
  • Registration failures should close the channel with the cause rather than emit a fake data value.
  • The adapter starts once per collector unless the resulting Flow is shared explicitly.

Why interviewers ask this: The interviewer is checking whether the candidate can bridge callback lifecycles into Flow without leaking registrations.

architecture-components

A ViewModel should own screen-level state and presentation logic without depending on a concrete view instance.

  • It survives configuration changes while its Activity, Fragment, or navigation entry remains logically alive.
  • It exposes immutable observable state and receives user actions through explicit methods or intents.
  • It delegates data access and domain rules to repositories or use cases instead of becoming a service locator.
  • It must not retain Activity, Fragment, View, or other short-lived Context references.

Why interviewers ask this: The interviewer is evaluating whether the candidate understands ViewModel ownership boundaries rather than treating it as a general-purpose singleton.

architecture-components

viewModelScope is a lifecycle-owned CoroutineScope cancelled when the ViewModel is cleared.

  • Its SupervisorJob lets sibling coroutines fail independently.
  • Its main dispatcher suits launching presentation work, while lower layers remain responsible for main-safe execution.
  • Coroutines survive configuration changes because the same ViewModel instance is retained.
  • It does not survive process death, so durable or restorable state needs another mechanism.

Why interviewers ask this: The interviewer is checking whether the candidate understands both the ownership guarantees and the limits of viewModelScope.

architecture-componentscoroutines

Prefer StateFlow for coroutine-based state pipelines, while LiveData remains useful for existing lifecycle-centric APIs and codebases.

  • StateFlow has an explicit current value and composes with the full Flow operator set.
  • LiveData automatically limits observer callbacks according to LifecycleOwner state.
  • StateFlow collection needs a lifecycle-aware adapter such as repeatOnLifecycle or collectAsStateWithLifecycle.
  • Conversion at the UI boundary is preferable to maintaining the same state independently in both types.

Why interviewers ask this: The interviewer is evaluating whether the candidate understands semantic and lifecycle differences instead of declaring one API universally superior.

immutabilitycoroutinesarchitecture-components

A ViewModel should mutate private state and expose only a read-only StateFlow to consumers.

  • MutableStateFlow keeps writes inside the component that owns state transitions.
  • asStateFlow prevents callers from assigning arbitrary values while avoiding a second stream.
  • Updating with the atomic update function prevents lost changes when transformations can race.
  • A single immutable state model makes rendering and state-transition tests deterministic.

Why interviewers ask this: The interviewer is checking whether the candidate applies state ownership and encapsulation to reactive UI models.

state

Persistent conditions belong in state, while non-repeatable effects need an explicit delivery model.

  • StateFlow is suitable for renderable facts such as content, selection, and loading status.
  • Replaying a navigation or snackbar event after recreation may repeat an action that already happened.
  • A Channel with receiveAsFlow gives single-consumer queued delivery, while a configured SharedFlow supports broadcast semantics.
  • If an effect represents an unhandled business fact, model and acknowledge that fact in state instead of hiding it as a transient event.

Why interviewers ask this: The interviewer is evaluating whether the candidate can prevent accidental event replay while preserving important state across UI recreation.

Locked questions

  • 21

    What problem does SavedStateHandle solve in a ViewModel?

    architecture-components
  • 22

    How do LifecycleOwner and LifecycleObserver support lifecycle-aware code?

    lifecycle
  • 23

    Why does a Fragment have both its own Lifecycle and a viewLifecycleOwner?

    android-componentslifecycle
  • 24

    What are the main responsibilities of a Jetpack Navigation graph?

    jetpack
  • 25

    How should data be passed between Navigation destinations?

  • 26

    How does Jetpack Navigation handle deep links?

    jetpacknavigation
  • 27

    How do popUpTo, inclusive, saveState, and restoreState affect the navigation back stack?

    navigation
  • 28

    How can a ViewModel be scoped to a nested navigation graph?

    architecture-components
  • 29

    How are multiple back stacks represented for bottom navigation?

    navigation
  • 30

    How do Room entities express keys and relational constraints?

    room
  • 31

    When should a Room DAO return a suspend result versus a Flow?

    roomcoroutines
  • 32

    What does @Transaction guarantee in Room?

    transactionsroom
  • 33

    How should Room schema migrations be defined?

    schemamigrationsroom
  • 34

    How do indexes affect Room query design?

    indexesqueriesroom
  • 35

    How do @Embedded, @Relation, and TypeConverter differ in Room?

    room
  • 36

    What kind of work is WorkManager designed for?

    designjetpack
  • 37

    How do constraints and retry policies work in WorkManager?

    resiliencejetpack
  • 38

    What are unique work and work chains in WorkManager?

    jetpack
  • 39

    When should CoroutineWorker be preferred over Worker?

    coroutines
  • 40

    How do Preferences DataStore and Proto DataStore differ?

    jetpack
  • 41

    Why should DataStore values be changed with edit or updateData?

    jetpack
  • 42

    What lifecycle and error rules should a DataStore instance follow?

    lifecyclejetpack
  • 43

    How do Hilt components and scopes determine dependency lifetime?

    componentsdependenciesdependency-injection
  • 44

    When do you use @Binds, @Provides, and qualifiers in Hilt or Dagger?

    dependency-injection
  • 45

    How does Dagger build and validate a dependency graph?

    validationdependenciesdependency-injection
  • 46

    How do MVVM and MVI organize UI state differently?

    architecture-patternsstate
  • 47

    How does Compose decide what to recompose?

    compose
  • 48

    How do remember and rememberSaveable differ in Compose?

    compose
  • 49

    What is state hoisting in Jetpack Compose?

    composehoistingjetpack
  • 50

    When should LaunchedEffect, DisposableEffect, and SideEffect be used?

  • 51

    How would you structure an offline-capable product list feature with MVVM, Room, and Jetpack Compose?

    jetpackarchitecture-patternscompose
  • 52

    How would you implement search-as-you-type without sending a request for every keystroke?

  • 53

    How would you implement an editable profile screen that survives rotation and reports save errors clearly?

  • 54

    How would you pass an item ID through Navigation without passing the whole object?

  • 55

    How would you add deep-link support to an authenticated detail screen?

  • 56

    How would you choose Hilt scopes for a repository, screen ViewModel, and temporary upload coordinator?

    architecture-componentsdependency-injection
  • 57

    How would you split a growing Android application into feature modules?

  • 58

    How would you model a payment form with MVI without replaying one-time navigation events?

    formsarchitecture-patterns
  • 59

    How would you design state hoisting for a reusable Compose date picker?

    composedesignhoisting
  • 60

    How would you implement paginated content backed by both a network API and Room?

    roomapi
  • 61

    How would you ship a Room schema change that adds a required column?

    schemaroom
  • 62

    How would you make an article screen useful when the device is offline?

  • 63

    How would you resolve a sync conflict when the same note was edited offline on two devices?

  • 64

    How would you implement reliable offline creation of records that must sync later?

  • 65

    How would you react to connectivity changes without making the app depend on a possibly stale network flag?

    react
  • 66

    How would you schedule a one-time background sync that must not run twice concurrently?

    concurrency
  • 67

    How would you implement periodic refresh without draining the battery?

  • 68

    How would you handle a user-visible upload that may take several minutes?

  • 69

    How would you cancel obsolete network work when a user leaves a screen?

  • 70

    How would you run two independent requests and cancel both if one critical request fails?

  • 71

    How would you investigate an ANR reported in production?

    anr
  • 72

    How would you prevent database or JSON parsing work from freezing the UI?

    database
  • 73

    How would you diagnose scrolling jank in a Compose screen?

    composerendering
  • 74

    How would you reduce unnecessary recomposition in a frequently updating Compose screen?

    compose
  • 75

    How would you keep a LazyColumn smooth when rows reorder and contain different layouts?

    ui
  • 76

    How would you reduce memory pressure from an image-heavy feed?

    memory
  • 77

    How would you find and fix an Activity memory leak caused by a listener?

    memoryandroid-components
  • 78

    How would you fix a coroutine that keeps a destroyed screen in memory?

    memorycoroutines
  • 79

    How would you prevent a Compose screen from leaking an Activity through a callback or remembered object?

    composecallbacksandroid-components
  • 80

    How would you restore a multi-step form after Android kills the app process?

    formsconcurrency
  • 81

    How would you fix duplicated data loading after every rotation?

  • 82

    How would you improve slow cold startup?

  • 83

    How would you speed up a Room query that blocks a large history screen?

    queriesroom
  • 84

    How would you investigate an API call that succeeds in Postman but fails in the Android app?

    api
  • 85

    How would you design network error handling for a screen with retry?

    designresilienceerror-handling
  • 86

    How would you unit-test a ViewModel that uses coroutines and StateFlow?

    coroutineshypothesis-testingarchitecture-components
  • 87

    When would you use a fake repository instead of MockK in a feature test?

    mocking
  • 88

    How would you test a Flow that emits loading, cached data, and refreshed data?

    caching
  • 89

    How would you test a Room migration before release?

    migrationsroom
  • 90

    How would you test a Compose form without coupling the test to layout details?

    formscomposeui
  • 91

    How would you fix a flaky Espresso test that proceeds before background work finishes?

    flakyui-testing
  • 92

    How would you test a WorkManager synchronization worker?

    jetpack
  • 93

    How would you build a CameraX preview and capture feature without leaking the camera?

  • 94

    How would you implement runtime permission handling for camera access?

    permissions
  • 95

    How would you make a custom Compose control accessible?

    compose
  • 96

    How would you persist user settings with DataStore without blocking startup?

    jetpack
  • 97

    How would you store and use an authentication token safely in an Android app?

    authtokens
  • 98

    How would you investigate a production crash using Firebase Crashlytics or Datadog?

    monitoring
  • 99

    How would you reduce Gradle build time in a modular Android project?

    build-tools
  • 100

    How would you release a risky mobile feature while preserving rollback options?

    rollback