HireHireInterview Quizzes › Android Developer

Android Developer Interview Questions

Think you're ready? These are the questions that actually decide Android Developer interviews. Warm up on Easy — then face the Hard round, where 95% of candidates crumble. 30 questions across 3 levels, instant score, completely free.

30Questions
3Difficulty levels
95%Fail the hard round
FreeInstant score
Easy
Warm-up · 10 Qs
Medium
Practical · 10 Qs
Hard
Brutal · 10 Qs
⚡ Take the Android Developer quiz — get your score →

The Android Developer interview questions

Below are the real questions, grouped by difficulty. Expand any one to reveal the correct answer and why — or take the timed quiz for a score you can share. Can you clear the Hard round?

Easy round 10 questions

Which component is used to run background operations that are guaranteed to execute even if the app is killed or the device restarts, respecting system battery constraints?
  • A. AsyncTask
  • B. WorkManager ✓
  • C. IntentService with a raw Thread
  • D. A plain Java Thread
Correct answer: B. WorkManager schedules deferrable, guaranteed background work that persists across app kills and reboots while respecting Doze/battery constraints.
In an Android Activity lifecycle, which callback is invoked when an Activity becomes visible to the user but is not yet in the foreground receiving input?
  • A. onCreate()
  • B. onStart() ✓
  • C. onResume()
  • D. onPause()
Correct answer: B. onStart() runs when the Activity becomes visible to the user, while onResume() is when it moves to the foreground and starts interacting.
What is the primary purpose of a ViewModel in Android's Jetpack Architecture Components?
  • A. To store and manage UI-related data in a lifecycle-conscious way that survives configuration changes ✓
  • B. To perform dependency injection across the app
  • C. To render the XML layout hierarchy on screen
  • D. To handle network serialization of JSON payloads
Correct answer: A. ViewModel holds and manages UI-related data in a lifecycle-aware way, surviving configuration changes like screen rotation.
In Kotlin coroutines, which dispatcher should be used for offloading blocking disk or network I/O operations?
  • A. Dispatchers.Main
  • B. Dispatchers.Default
  • C. Dispatchers.IO ✓
  • D. Dispatchers.Unconfined
Correct answer: C. Dispatchers.IO is optimized for I/O-bound work like network and disk operations, using a shared pool sized for blocking tasks.
Your app crashes with NetworkOnMainThreadException. What is the correct fix?
  • A. Wrap the network call in a try-catch block
  • B. Increase the app's heap size in the manifest
  • C. Move the network call off the main thread using a coroutine or background thread ✓
  • D. Add the INTERNET permission to the manifest
Correct answer: C. NetworkOnMainThreadException occurs when networking runs on the UI thread, so the call must be moved to a background thread or coroutine.
Which class is the recommended local persistence library that provides a compile-time-verified abstraction layer over SQLite?
  • A. SharedPreferences
  • B. Room ✓
  • C. Retrofit
  • D. Gson
Correct answer: B. Room is Jetpack's ORM over SQLite that verifies SQL queries at compile time and reduces boilerplate.
In a RecyclerView, what is the main role of the ViewHolder pattern?
  • A. It caches view references so findViewById is not called repeatedly during scrolling ✓
  • B. It automatically downloads images from the network
  • C. It stores the entire dataset in memory for fast access
  • D. It manages the Activity lifecycle for each list item
Correct answer: A. The ViewHolder caches child view references so RecyclerView avoids costly repeated findViewById lookups while recycling views during scroll.
What does adding android:exported="true" to an Activity in the manifest do?
  • A. Encrypts the Activity's data
  • B. Allows components of other apps to launch that Activity ✓
  • C. Marks the Activity as the app's launcher screen
  • D. Prevents the Activity from being destroyed on rotation
Correct answer: B. android:exported="true" makes a component launchable by other apps; on Android 12+ it must be explicitly declared for components with intent filters.
Which permission model applies to "dangerous" permissions like ACCESS_FINE_LOCATION on Android 6.0 (API 23) and above?
  • A. They are granted automatically at install time
  • B. They must be requested at runtime and approved by the user ✓
  • C. They can only be granted by rooting the device
  • D. They are declared only in code, never in the manifest
Correct answer: B. Since API 23, dangerous permissions must be requested at runtime and explicitly granted by the user, not just declared in the manifest.
In Jetpack Compose, what is the purpose of the remember function?
  • A. To persist data to disk across app launches
  • B. To store a value across recompositions so it is not recalculated each time ✓
  • C. To launch a coroutine tied to a composable's lifecycle
  • D. To trigger a network request when a composable enters the screen
Correct answer: B. remember stores a value in the composition so it is retained across recompositions rather than being recomputed on every recomposition.

Medium round 10 questions

In an Activity, when should you typically register a location-updates listener and unregister it to avoid battery drain and leaks while following the lifecycle correctly?
  • A. Register in onCreate(), unregister in onDestroy()
  • B. Register in onResume(), unregister in onPause() ✓
  • C. Register in onStart(), unregister in onStop() only if the app is being killed
  • D. Register in onRestoreInstanceState(), unregister in onSaveInstanceState()
Correct answer: B. onResume/onPause is the recommended pair for active foreground resources like location updates, so the listener runs only while the Activity is interacting with the user.
You need to run a network call and update a TextView with the result using Kotlin coroutines in a ViewModel. Which approach is correct?
  • A. Launch on viewModelScope with Dispatchers.IO for the network call, then post the result to LiveData/StateFlow observed by the UI ✓
  • B. Call the network directly on Dispatchers.Main so the TextView updates immediately
  • C. Use GlobalScope.launch so the coroutine survives configuration changes
  • D. Run the network call with runBlocking on the main thread to keep code sequential
Correct answer: A. viewModelScope is lifecycle-aware and IO work belongs on Dispatchers.IO, with results exposed via LiveData/StateFlow for the UI to observe safely on the main thread.
Your app crashes with NetworkOnMainThreadException. What is the most likely cause?
  • A. You forgot to add the INTERNET permission in the manifest
  • B. You performed a synchronous network request on the UI thread ✓
  • C. You used the wrong OkHttp version
  • D. You called findViewById before setContentView
Correct answer: B. NetworkOnMainThreadException is thrown when a networking operation runs on the main/UI thread, which Android forbids to keep the UI responsive.
In a RecyclerView adapter, why is it important to implement DiffUtil (or ListAdapter) rather than calling notifyDataSetChanged() on every list update?
  • A. notifyDataSetChanged() is deprecated and no longer works
  • B. DiffUtil computes minimal changes so only affected items rebind and animate, improving performance ✓
  • C. notifyDataSetChanged() requires a separate background thread to run
  • D. DiffUtil is required to inflate the item layouts
Correct answer: B. DiffUtil calculates the minimal set of item changes so only modified rows are rebound and animated, avoiding the full rebind that notifyDataSetChanged() forces.
What is the primary reason to use a ViewModel to hold UI state instead of storing it directly in the Activity?
  • A. ViewModel encrypts the data automatically
  • B. ViewModel survives configuration changes like screen rotation, so state isn't lost and doesn't need re-fetching ✓
  • C. ViewModel runs all its code on a background thread by default
  • D. ViewModel prevents the Activity from ever being destroyed
Correct answer: B. A ViewModel outlives configuration changes such as rotation, retaining UI state without re-fetching and reducing boilerplate around onSaveInstanceState.
You want a value that emits updates and always has a current value for Compose/Flow-based UI, replacing LiveData in a repository. Which type fits best?
  • A. SharedFlow with replay set to 0
  • B. StateFlow initialized with a default value ✓
  • C. A plain suspend function returning the value once
  • D. Channel with unlimited capacity
Correct answer: B. StateFlow is a hot, state-holder flow that always has a current value and emits updates, making it the idiomatic replacement for LiveData in coroutine/Compose code.
Which Gradle dependency configuration should you use for a library that is needed at compile time but should NOT be exposed to modules that depend on your module?
  • A. api
  • B. implementation ✓
  • C. compileOnly
  • D. runtimeOnly
Correct answer: B. implementation keeps the dependency off the public/compile classpath of downstream modules, improving build times and encapsulation, whereas api leaks it transitively.
A user reports that your app's data survives an uninstall/reinstall on Android 6.0+ and restores automatically. Which feature is most likely responsible, and how do you exclude sensitive files?
  • A. SharedPreferences encryption; disable it in code
  • B. Auto Backup for Apps; use android:fullBackupContent / dataExtractionRules to exclude files ✓
  • C. Scoped Storage; add a .nomedia file
  • D. WorkManager backup; cancel the periodic work
Correct answer: B. Android's Auto Backup automatically backs up app data to the cloud, and you exclude files via the fullBackupContent (or dataExtractionRules) XML rules.
For a task that must run reliably even if the app is closed or the device reboots, such as syncing data to a server periodically, which API is the recommended choice?
  • A. A background Thread started in the Application class
  • B. AsyncTask with a repeating loop
  • C. WorkManager with a PeriodicWorkRequest ✓
  • D. A Handler with postDelayed on the main looper
Correct answer: C. WorkManager is the recommended API for deferrable, guaranteed background work that persists across app restarts and reboots and respects system constraints.
In an Android manifest, you declare an <activity> that should be launchable by other apps via an implicit intent. What must you include?
  • A. android:exported="false" and no intent-filter
  • B. An <intent-filter> with the appropriate action/category and android:exported="true" ✓
  • C. A <uses-permission> tag for the activity
  • D. android:launchMode="singleTask" only
Correct answer: B. To respond to implicit intents from other apps the activity needs a matching intent-filter and must be explicitly exported (required to be set on Android 12+).

Hard round 10 questions

A composable receives `data: List<User>` as a parameter and is never skipped during recomposition, even when the parent recomposes with an identical list reference. All properties of `User` are `val`. What is the MOST likely reason Compose treats this parameter as unstable?
  • A. `List<User>` is an interface type that Compose cannot prove immutable, so it infers the parameter as unstable ✓
  • B. Passing any `List` always forces a recomposition because collections are compared structurally, not by reference
  • C. `User` must be annotated with `@Composable` for the compiler to consider it stable
  • D. The composable is missing the `@Stable` annotation on its function declaration
Correct answer: A. `List` is an interface with no immutability guarantee, so the Compose compiler infers it unstable; using `ImmutableList`/`@Immutable` or a `kotlinx.collections.immutable` type fixes it.
Inside a `ViewModel` you write `viewModelScope.launch { val a = async { apiA() }; val b = async { apiB() }; combine(a.await(), b.await()) }`. `apiB()` throws. What happens to `apiA()`'s coroutine and how does the exception surface?
  • A. `apiA()` keeps running to completion; the exception is silently swallowed by `async`
  • B. `apiA()` is cancelled and the exception propagates to `viewModelScope`, cancelling the whole scope ✓
  • C. Only the `async { apiB() }` fails; `a.await()` returns normally and the crash is deferred until `b.await()`
  • D. Both `async` calls are cancelled but the exception is caught automatically by `viewModelScope`'s default handler
Correct answer: B. Because the `async` children share the `launch` job under structured concurrency, `apiB`'s failure cancels its siblings (including `apiA`) and propagates up, cancelling `viewModelScope`.
You expose one-time navigation events from a ViewModel using `val events = MutableStateFlow<Event?>(null)` collected in the UI with `collect { ... }`. After a config change (Activity recreated, ViewModel retained), users report the same navigation event fires again. What is the root cause?
  • A. `MutableStateFlow` drops emissions, so the re-collection reads a stale buffered event by mistake
  • B. `StateFlow` always replays its current value to new collectors, so the retained non-null event is re-delivered on re-subscription ✓
  • C. `collect` on the main thread cannot receive events after a config change unless you use `collectLatest`
  • D. `MutableStateFlow` requires `distinctUntilChanged` to be disabled to avoid duplicate events
Correct answer: B. `StateFlow` has replay-1 semantics and always emits its current value to new collectors, so retaining a non-null event re-fires it; a `SharedFlow` with replay 0 (or Channel) is the correct primitive for events.
Given `flow { emit(1); emit(2) }.map { heavyCpu(it) }.flowOn(Dispatchers.Default).collect { updateUi(it) }` collected from `Dispatchers.Main`, on which dispatcher does `heavyCpu` run and on which does `updateUi` run?
  • A. Both `heavyCpu` and `updateUi` run on `Dispatchers.Default`
  • B. `heavyCpu` runs on `Dispatchers.Default`; `updateUi` runs on `Dispatchers.Main` ✓
  • C. Both run on `Dispatchers.Main` because `collect` dictates the whole chain's context
  • D. `heavyCpu` runs on `Dispatchers.Main`; `updateUi` runs on `Dispatchers.Default`
Correct answer: B. `flowOn` changes context only for upstream operators (the `map` and the source), while the collector runs in the collecting coroutine's context (`Main`).
A `LaunchedEffect(Unit) { while(true) { delay(1000); tick() } }` is placed inside an `if (isVisible)` block. A colleague changes it to `LaunchedEffect(isVisible)` and moves it outside the `if`. What behavioral difference results when `isVisible` toggles true→false→true?
  • A. The keyed version restarts `tick()` cleanly on each toggle; the original leaves the coroutine running while invisible ✓
  • B. Both versions behave identically because `LaunchedEffect` is keyed by composition position anyway
  • C. The keyed version never restarts because `isVisible` is a Boolean, not a stable key
  • D. The original version keeps `tick()` running forever even when removed from composition
Correct answer: A. Placing `LaunchedEffect` inside the `if` cancels it when the block leaves composition; keying on `isVisible` outside keeps it in composition but cancels/relaunches whenever the key changes.
Consider: `class Analytics(private val activity: Activity)` stored in a `companion object`-held singleton created from `onCreate`. LeakCanary flags the Activity. What reference chain keeps the Activity alive across rotation?
  • A. The `Activity`'s window holds the singleton, forming a cycle the GC cannot collect
  • B. The static `companion object` → singleton → `Analytics` → `activity` field, which the GC roots never release across recreation ✓
  • C. Rotation creates a new Activity that inherits the old one's context, doubling the leak
  • D. `Analytics` implements `LifecycleObserver`, so the lifecycle registry pins the Activity
Correct answer: B. A static (companion) reference is a GC root; because it transitively holds the destroyed Activity via `Analytics.activity`, the Activity can never be collected.
You have `sharedFlow = MutableSharedFlow<Int>(replay = 0, extraBufferCapacity = 0)` with the default `SUSPEND` overflow strategy. A producer calls `sharedFlow.tryEmit(x)` while there are zero active collectors. What is the result?
  • A. `x` is buffered and delivered to the next collector that subscribes
  • B. `tryEmit` returns `false` and `x` is dropped because there is no buffer or replay to hold it ✓
  • C. `tryEmit` suspends until a collector appears
  • D. `tryEmit` throws an `IllegalStateException` due to buffer overflow
Correct answer: B. With replay 0 and no buffer, `tryEmit` cannot suspend and has nowhere to place the value with no collectors, so it returns `false` and the value is dropped.
An offline-first app uses Room as the single source of truth. Two devices edit the same record offline; both later sync. To resolve conflicts deterministically while preserving concurrent edits from being silently lost, which strategy best fits a senior design?
  • A. Last-write-wins using each device's local wall-clock timestamp on the mutation
  • B. Server-assigned version numbers with optimistic concurrency: reject stale writes and surface a merge, using per-field or vector clocks to detect true concurrency ✓
  • C. Always prefer the device with the lower device ID to break ties
  • D. Disable offline writes on all but one designated primary device
Correct answer: B. Wall clocks skew across devices; server versioning/vector clocks detect genuine concurrent edits and let you merge rather than blindly overwrite, which is the consistency-preserving senior choice.
`collectLatest { item -> processLongRunning(item) }` is applied to a flow emitting rapidly. `processLongRunning` does blocking I/O with no suspension points. What actually happens on fast emissions?
  • A. Each new emission cancels and restarts `processLongRunning`, so only the last item's work completes
  • B. New emissions cannot cancel the in-flight block because cancellation is cooperative and the blocking call never checks for it ✓
  • C. The flow buffers all emissions and runs them sequentially to completion
  • D. `collectLatest` throws because the collector cannot keep up with the producer
Correct answer: B. `collectLatest` cancels the previous block on a new emission, but cancellation is cooperative—code with no suspension points never observes the cancel, so it runs to completion regardless.
An app has a cold start of 1.8s. Systrace shows most time in class loading and JIT during the first frames of the main screen. Which remediation most directly targets this symptom?
  • A. Move all initialization into a `ContentProvider` to run it earlier
  • B. Ship a Baseline Profile so hot code paths are AOT-compiled at install time instead of JIT-compiled at runtime ✓
  • C. Increase the heap size in the manifest to reduce GC pauses
  • D. Wrap startup work in `WorkManager` with an expedited request
Correct answer: B. Baseline Profiles pre-compile (AOT) the critical startup/scroll code paths at install, eliminating interpretation/JIT overhead that dominates the first frames of cold start.

Prep for another role

Questions are original, written and independently verified for HireHire's role interview quizzes. They reflect the kind of knowledge Android Developer interviews test, not any specific company's questions. HireHire maps live tech & IT jobs across India, updated regularly. Last updated: July 2026.