RecallDeck
Interview track

Android Developer interview prep

A spaced-repetition deck of 161+ Android Developer interview questions — organised by topic and difficulty, and resurfaced right before you'd forget. Preview a few cards below, then sign in to study the whole track on an Anki-style SM-2 schedule.

161 cards10 topics

Free · sign in with GitHub · your progress stays yours.

What's covered

Every topic in this track, grouped the way you'd study it.

Kotlin & the Type System

11 cards
Kotlin & Types

Coroutines & Flow

12 cards
Coroutines & Flow

Components & Lifecycle

10 cards
Components & Lifecycle

UI: Views & Jetpack Compose

11 cards
Views & Compose

Android App Architecture

10 cards
Architecture

Networking & Persistence

10 cards
Networking & Persistence

Performance & Internals

10 cards
Performance & Internals

Testing & Tooling

9 cards
Testing & Tooling

CS Fundamentals

43 cards
Data Structures & Algorithms

Behavioral

35 cards
Behavioral

Sample questions

A few cards from the deck — reveal each answer, then sign in to study the full set on a schedule.

How does val differ from var, and does val make an object immutable?

Short answer: var is a reassignable reference, val is read-only: you cannot reassign it. But val freezes only the reference, not the object's contents: val list = mutableListOf<Int>() still accepts list.add(1). Real immutability is a read-only type plus val.

In depth:

val a = mutableListOf(1, 2)
a.add(3)          // ✅ same reference, contents change
// a = mutableListOf() // ❌ val cannot be reassigned

var n = 0
n = 1             // ✅ var is reassignable

val now: Long get() = System.currentTimeMillis() // val with a getter — not a constant!
const val API = "v1"  // const — compile time, primitives and String only
  1. val ≠ immutable — it only guarantees a stable reference; mutability depends on the type (List vs MutableList).
  2. val with a custom getter — is computed on every access, and its value can differ from call to call.
  3. const val — is inlined by the compiler at the use site; allowed only at top level or in an object/companion, with a primitive or String value.

⚠️ Common mistake: treating val as a synonym for immutable. val is about the reference, not the depth of the object.

What is a coroutine, and how does suspend differ from blocking a thread?

Short answer: A coroutine is a lightweight unit of concurrency on top of threads. Suspension parks the coroutine and frees the thread for other work; blocking keeps the thread busy. That is why thousands of coroutines can live on a single thread.

In depth:

// Blocking: the thread is busy and useless
fun blocking() {
    Thread.sleep(1000)   // the thread sleeps — unavailable to anyone
}

// Suspension: the coroutine parks, the thread stays free
suspend fun suspending() {
    delay(1000)          // meanwhile the thread runs other coroutines
}

fun main() = runBlocking {
    repeat(100_000) { launch { delay(1000) } }  // fine even on one thread
    // 100,000 threads with Thread.sleep(1000) — OutOfMemoryError
}
  1. Suspension ≠ blockingdelay hands control back to the dispatcher; Thread.sleep holds the thread hostage.
  2. Lightweight — a coroutine is a heap object (continuation + state), not a ~1 MB stack like a thread.
  3. Cooperative — a coroutine yields its thread only at suspension points.

⚠️ Common mistake: saying a suspend function runs on a background thread. It does not: suspend by itself never switches threads — the dispatcher decides where the code runs.

Walk through the full Activity lifecycle — which callbacks fire and what each is for?

Short answer: Three "up" callbacks — onCreate → onStart → onResume — bring the screen to the user; three "down" ones — onPause → onStop → onDestroy — take it away. onRestart fires when a stopped Activity comes back to the screen.

In depth:

     onCreate()    ← created: setContentView, init, read savedInstanceState

     onStart()     ← became visible

     onResume()    ← in the foreground, receiving input

   [ RESUMED — running ]

     onPause()     ← lost focus (partially covered)

     onStop()      ← fully hidden  ──onRestart()→onStart() when it returns

     onDestroy()   ← destroyed (finish or low memory)
  1. onCreate — the only mandatory one; layout inflation, initialization, restoring from savedInstanceState.
  2. onStart / onStop — the visibility boundary: register and unregister whatever only a visible screen needs.
  3. onResume / onPause — the focus and input boundary; onPause must be short — the next screen won't appear until it returns.
  4. onDestroy — the finale; may never arrive if the process is killed, so save critical things earlier.

⚠️ Common mistake: treating onDestroy as a guaranteed place to save. On process death it never runs — save in onSaveInstanceState / onStop.

What is recomposition and what triggers it?

Short answer: Recomposition is re-invoking @Composable functions when the state they read changes. It is triggered by a write to an observable State (mutableStateOf, a StateFlow via collectAsState, etc.) that was read inside the composable. Only the functions that read that particular value re-run.

In depth:

@Composable
fun Counter() {
    var count by remember { mutableStateOf(0) }
    Text("Clicks: $count")           // reads count → re-invoked
    Button(onClick = { count++ }) {  // writes count → recomposition
        Text("Tap")                  // doesn't read count → skipped
    }
}
  1. The trigger is not "any change" — it is a write to snapshot state that was read in a specific composable. Compose tracks reads and invalidates exactly the affected scopes.
  2. Optimistic — if state changes again mid-recomposition, the current pass is discarded and restarted.
  3. Unordered and possibly parallel — the execution order of sibling composables is not guaranteed; don't rely on side effects in the body.

⚠️ Common mistake: a plain var without mutableStateOf is not state; Compose can't see its change and won't recompose — the UI freezes.

What is MVVM, and why must a ViewModel never hold a reference to a View or Context?

Short answer: MVVM splits a screen into three roles: Model (data and logic), View (Activity/Fragment/Composable — it only renders and forwards actions), and ViewModel (holds state and survives View recreation). The ViewModel knows nothing about the concrete View — it just publishes state the View subscribes to.

In depth:

┌────────┐   subscribe to state  ┌───────────┐   request data  ┌───────┐
│  View  │ ◄─────────────────── │ ViewModel │ ──────────────► │ Model │
│ (dumb) │ ── actions (events) ─►│  (state)  │ ◄─── data ────── │(repo) │
└────────┘                       └───────────┘                 └───────┘
  1. View is a dumb renderer — it takes ready-made state and draws it; no business logic, just "show what I was given".
  2. ViewModel holds state — it survives rotation (the ViewModelStoreOwner keeps it, rather than recreating it with the Activity).
  3. Model — repositories, DB, network; the ViewModel talks to these, not the View directly.

⚠️ Common mistake: storing a View, Activity, or Context in a ViewModel field. The ViewModel outlives the View — after a rotation the reference points at a destroyed Activity, which is a memory leak. Need a Context? Use AndroidViewModel.getApplication() or inject @ApplicationContext.

What is the Retrofit + OkHttp stack made of, and what does each layer do?

Short answer: Retrofit is the top, declarative layer: it turns an annotated Kotlin interface into HTTP requests and parses the response via a Converter. OkHttp beneath it is the actual HTTP client: sockets, connection pool, cache, interceptors, timeouts, retries. Retrofit does not work without OkHttp — it is its transport.

In depth:

┌─────────────────────────────────────────┐
│ ApiService (interface + @GET/@POST)      │  ← your code
├─────────────────────────────────────────┤
│ Retrofit: annotations → Request,         │
│           Converter (JSON ↔ data class)  │
├─────────────────────────────────────────┤
│ OkHttp: interceptors, connection pool,   │
│         cache, timeouts, TLS, retry      │
├─────────────────────────────────────────┤
│ Socket / network                         │
└─────────────────────────────────────────┘
  1. Retrofit — understands suspend and returns a ready data class; it does not parse JSON itself.
  2. Converter — kotlinx.serialization / Moshi / Gson; this is what maps JSON ↔ object.
  3. OkHttp — all the networking machinery; one OkHttpClient per app so the pool and cache are reused.
  4. CallAdaptersuspend-function support out of the box.

⚠️ Common mistake: creating a new OkHttpClient/Retrofit per request. That throws away the connection pool and cache; the client should be a single instance for the whole app.

Ready to make it stick?

Start your first session in under a minute. Your future self, mid-interview, will thank you.

Questions about this track

How should I prepare for an Android Developer interview?

Study the concepts you'll be asked to explain, not just the ones you can code. RecallDeck's Android Developer track gives you 161+ curated interview questions and resurfaces each one with an Anki-style SM-2 schedule right before you'd forget it — so the answers are still there under pressure on interview day.

What topics does the Android Developer track cover?

The Android Developer track is organised into the core areas Android Developer interviews actually test, grouped by topic and by difficulty (Concept, Junior, Middle, Senior). You can preview the full outline and sample questions above before signing in.

Is spaced repetition effective for Android Developer interview prep?

Yes. Actively recalling an answer and grading yourself honestly builds far more durable memory than re-reading notes. RecallDeck schedules each Android Developer card to reappear at the moment you're about to forget it, so your daily reviews shrink while your recall holds.

Is the Android Developer track free?

Yes — the Android Developer track and the full SM-2 scheduler are free, with 20 new cards a day. Sign in with GitHub and your progress syncs to your account. RecallDeck Pro ($5/month or $29/year) raises the daily budget and adds cram mode.

Other interview tracks

RecallDeckSpaced-repetition interview prep