Skip to content
All posts
June 6, 20265 min read

Android Developer Interview Questions: The Hard Ones and How to Answer Them

Senior Android interview questions test deep understanding, not syntax recall. Here are 15 hard questions covering architecture, concurrency, performance, and testing — with the answers that demonstrate senior-level thinking.

AndroidCareerKotlinArchitecture
Share:

Junior interviews test if you know the APIs. Senior interviews test if you understand the tradeoffs. Here are the questions that separate prepared candidates from the rest.


Architecture Questions

Q: What's the difference between

code
StateFlow
and
code
SharedFlow
, and when would you use each?

code
StateFlow
is a hot flow with a current value — always has a value, replays the latest to new collectors. Use it for UI state that a screen needs immediately on subscription.

code
SharedFlow
is a hot flow without a required current value. Configurable replay cache (0 by default). Use it for events that should be consumed once — navigation, snackbar messages, error notifications that shouldn't show again when the collector re-subscribes.

The key distinction: if a new subscriber should see the latest state, use

code
StateFlow
. If events should not replay, use
code
SharedFlow
with
code
replay = 0
.


Q: How does Hilt handle scope and what are the different component scopes?

Hilt components have a hierarchy:

  • code
    SingletonComponent
    — lives as long as the Application. Use for repositories, database, network client.
  • code
    ActivityRetainedComponent
    — survives configuration changes, tied to ViewModel lifecycle. Use for ViewModel-scoped dependencies.
  • code
    ActivityComponent
    — tied to Activity lifecycle. Use for dependencies needing Activity context.
  • code
    ViewModelComponent
    — tied to a specific ViewModel. Use for per-ViewModel state.
  • code
    FragmentComponent
    ,
    code
    ViewComponent
    ,
    code
    ServiceComponent
    — tied to their respective lifecycle.

A dependency in

code
SingletonComponent
is shared across the entire app. A dependency in
code
ActivityComponent
is created fresh for each Activity instance.


Q: Explain the Clean Architecture layers and what shouldn't cross between them.

Domain layer contains business rules (use cases, domain models, repository interfaces). It has zero Android or framework imports — pure Kotlin.

Data layer implements repository interfaces. It knows about Room, Retrofit, and how to map between network/database types and domain models.

Presentation layer (ViewModel + UI) depends on use cases from Domain, not on Data implementations directly.

What shouldn't cross:

  • Domain should not import Android SDK classes or third-party networking/DB libraries
  • ViewModel should not import Room DAOs directly — access through use cases/repositories
  • Network DTOs should not appear in ViewModel — map to domain models in the repository

Coroutines and Concurrency

Q: What happens when you launch a coroutine with

code
GlobalScope
and why is it problematic?

code
GlobalScope
coroutines are tied to the application lifetime — they never cancel unless explicitly cancelled. Problems:

  1. Memory leaks: references held by the coroutine prevent garbage collection
  2. No lifecycle awareness: the coroutine continues after the Activity/ViewModel is destroyed
  3. No structured concurrency: exceptions aren't propagated to a parent, they crash the app silently (or get swallowed depending on the exception handler)

Solution: use

code
viewModelScope
(cancels with ViewModel),
code
lifecycleScope
(cancels with lifecycle), or an injected application scope with explicit cancellation.


Q: What's the difference between

code
launch
and
code
async
/
code
await
?

code
launch
starts a coroutine that returns a
code
Job
. The result of the work is delivered through side effects (updating state, calling callbacks). Fire-and-forget.

code
async
starts a coroutine that returns a
code
Deferred<T>
. You call
code
.await()
to get the result. Use when you need the return value.

When you need two concurrent operations and both results:

kotlin
val aDeferred = async { fetchUserProfile() }
val bDeferred = async { fetchUserTasks() }
val profile = aDeferred.await()
val tasks = bDeferred.await()

If you used

code
launch
for both, you'd need callback coordination.
code
async
/
code
await
makes concurrent work with return values clean.


Performance

Q: How do you identify and fix recomposition performance problems in Compose?

First, identify: use the Layout Inspector's Recomposition Counts view or add

code
SideEffect { println("recomposing") }
to suspect composables.

Common causes:

  1. Unstable types as parameters:
    code
    List<T>
    is unstable. Use
    code
    ImmutableList
    from
    code
    kotlinx.collections.immutable
    .
  2. Lambda instances changing on each recomposition: use
    code
    remember { lambda }
    or
    code
    viewModel::method
    .
  3. Reading state at a high level: read state as low in the tree as possible — only composables that read a specific state recompose when it changes.
  4. Missing keys in
    code
    LazyColumn
    : without stable keys, reordering looks like replacing all items.

The fix is often: make parameters stable, move state reads lower, and add keys to lists.


Testing

Q: How do you test a ViewModel that depends on a Flow from a repository?

Three approaches, depending on complexity:

  1. Fake repository with a FakeFlow: inject a

    code
    FakeTaskRepository
    that has a
    code
    MutableStateFlow
    . In the test, emit to the fake flow and assert on ViewModel state.

  2. Turbine for Flow testing:

kotlin
viewModel.uiState.test {
    assertEquals(Loading, awaitItem())
    fakeRepository.emitTasks(testTasks)
    assertEquals(Success(testTasks), awaitItem())
    cancelAndIgnoreRemainingEvents()
}
  1. MainDispatcherRule: ViewModels use
    code
    Dispatchers.Main
    . In JVM tests, replace it with
    code
    UnconfinedTestDispatcher
    via
    code
    MainDispatcherRule
    .

All three are needed together for deterministic ViewModel tests.


Q: What's the difference between testing with Robolectric vs on an emulator?

Robolectric runs Android SDK classes on the JVM without an emulator. Tests run fast (seconds vs minutes). Good for: ViewModel tests, DAO tests, anything that doesn't need hardware or actual rendering.

Instrumentation tests run on a real device or emulator. They test against the real Android framework, real rendering, real GPU. Required for: UI tests (Espresso/Compose testing), anything touching hardware, tests where Android behavior differs from Robolectric emulation.

Robolectric occasionally has discrepancies from real Android behavior. Critical flows should be tested on-device, not just in Robolectric.


Questions You Should Ask

At the end of every Android interview, ask:

  • "What's your target and min SDK, and how do you handle API-level differences?"
  • "What's your test strategy — what do you automate vs keep manual?"
  • "How does the team handle the Compose/XML migration if it's ongoing?"

These questions reveal the team's technical maturity more than the interview questions do.


Takeaways

  • Architecture questions test understanding of why the separation exists, not just what the layers are
  • Coroutine questions test structured concurrency understanding —
    code
    GlobalScope
    is a red flag
  • Performance questions in Compose often reduce to stability and state reading levels
  • Testing questions assess whether you understand when to use which tool (unit vs integration vs UI)
  • The questions you ask at the end matter — they show you evaluate team health, not just accept any offer
Share:
S

Sudarshan Chaudhari

AI Systems Builder / Product Engineer

Bangkok, Thailand

Solo Android developer with 13+ years in QA, building Android apps, AI automation systems, and developer tools at SudarshanTechLabs.

Stay updated

Get new posts on Android, Kotlin, and solo dev straight to your inbox.

Newsletter preferences

Related Apps

MyFamilyTracker

Real-time family location sharing — Firebase Realtime DB for sub-second propagation, WorkManager + ForegroundService for OS-compliant background collection, geofencing via Google Maps API.

Building something? Available for Android dev and QA consulting.

Work with me

Comments — powered by Giscus

Apps tagged with this