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. 80 questions across 3 levels, instant score, completely free.

80Questions
3Difficulty levels
95%Fail the hard round
FreeInstant score
Easy
Warm-up · 20 Qs
Medium
Practical · 30 Qs
Hard
Brutal · 30 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 20 questions

You perform a network request directly on the main thread and the app crashes with NetworkOnMainThreadException. Why does Android forbid this?
  • A. Blocking the main thread would freeze the UI ✓
  • B. Network calls need root permission
  • C. The main thread cannot open sockets
  • D. Only services may access the network
Correct answer: A. The main (UI) thread must stay responsive; long network work there would block rendering and trigger ANRs.
An Activity is visible but partially covered by a dialog from another app. Which lifecycle state describes it?
  • A. Paused (onPause called, not onStop) ✓
  • B. Stopped
  • C. Destroyed
  • D. Created but not started
Correct answer: A. A partially obscured but visible Activity is paused; onStop only runs when it is no longer visible.
You rotate the device and your Activity loses its typed-in but unsaved text. What is the correct fix?
  • A. Save state in onSaveInstanceState or a ViewModel ✓
  • B. Disable rotation permanently
  • C. Move the code to onDestroy
  • D. Use a static variable to hold the text
Correct answer: A. Configuration changes recreate the Activity; onSaveInstanceState or a ViewModel preserves transient UI state.
In a RecyclerView, item views show wrong data while scrolling fast. What is the usual root cause?
  • A. Recycled views not being fully reset in onBindViewHolder ✓
  • B. Too many items in the list
  • C. Using a LinearLayoutManager
  • D. Not calling notifyDataSetChanged enough
Correct answer: A. Recycled ViewHolders retain old state; every field must be set in onBindViewHolder or stale data appears.
You launch a coroutine with `viewModelScope.launch` for a DB read. What happens when the ViewModel is cleared?
  • A. The coroutine is automatically cancelled ✓
  • B. It keeps running forever
  • C. It moves to the main thread
  • D. It restarts from the beginning
Correct answer: A. viewModelScope is tied to the ViewModel lifecycle and cancels its coroutines on onCleared.
Your app needs the user's precise location. What must happen at runtime on Android 6.0+?
  • A. Request the dangerous permission at runtime and handle the user's choice ✓
  • B. Only declare it in the manifest
  • C. Nothing, location is granted by default
  • D. Add it to gradle dependencies
Correct answer: A. Dangerous permissions like fine location must be requested at runtime and can be denied by the user.
You observe a LiveData in a Fragment and pass `viewLifecycleOwner`. Why not pass `this`?
  • A. The Fragment's view has a shorter lifecycle than the Fragment itself ✓
  • B. this is not a LifecycleOwner
  • C. viewLifecycleOwner is faster
  • D. LiveData requires a nullable owner
Correct answer: A. Using viewLifecycleOwner avoids updating a destroyed view when the Fragment's view is recreated but the Fragment survives.
In Jetpack Compose, a Composable does not update when your plain `var count` changes. What do you need?
  • A. Hold count in a mutableStateOf state variable ✓
  • B. Call invalidate() manually
  • C. Make count a global variable
  • D. Wrap it in a LaunchedEffect
Correct answer: A. Compose recomposes only when it reads observable state such as mutableStateOf.
You store the user's auth token. Which storage is appropriate for a small key-value secret-ish value?
  • A. EncryptedSharedPreferences ✓
  • B. A plain text file in external storage
  • C. A public static field
  • D. The app's log output
Correct answer: A. EncryptedSharedPreferences keeps small sensitive values encrypted at rest in app-private storage.
An implicit Intent with ACTION_VIEW and a web URL is fired but the app crashes. What guard prevents this?
  • A. Check resolveActivity/handle ActivityNotFoundException before starting ✓
  • B. Add more Intent extras
  • C. Use an explicit component name
  • D. Request INTERNET permission
Correct answer: A. If no app can handle the implicit Intent, startActivity throws; you should verify a handler exists first.
Your list shows a jank/stutter when loading images from the network. Which approach best fixes it?
  • A. Load images asynchronously with a library like Glide/Coil and cache them ✓
  • B. Decode bitmaps on the main thread
  • C. Increase the item view size
  • D. Disable the RecyclerView animator
Correct answer: A. Off-thread loading with caching keeps the UI thread free, eliminating scroll jank.
You need work to run reliably even if the user closes the app or reboots the phone (e.g. syncing data). Which API fits?
  • A. WorkManager ✓
  • B. A plain Thread
  • C. AsyncTask
  • D. A coroutine in the Activity
Correct answer: A. WorkManager guarantees deferrable, persistent background work that survives process death and reboots.
Two Fragments need to share the same data during their lifetime in one Activity. What is the idiomatic solution?
  • A. A ViewModel scoped to the Activity, shared by both Fragments ✓
  • B. Static singletons
  • C. Passing data via SharedPreferences
  • D. Bundle arguments copied back and forth
Correct answer: A. An activity-scoped ViewModel is shared across its Fragments, providing a single source of truth.
Your Room DAO method runs a query and you call it on the main thread; the app throws. What is the fix?
  • A. Make it a suspend function or return LiveData/Flow so Room runs it off the main thread ✓
  • B. Add allowMainThreadQueries permanently
  • C. Move the DAO to a Service
  • D. Use a raw SQLiteDatabase instead
Correct answer: A. Room blocks main-thread queries by default; suspend/Flow/LiveData move the work off the UI thread.
You keep a reference to an Activity Context inside a long-lived singleton. What problem does this cause?
  • A. A memory leak because the Activity can't be garbage collected ✓
  • B. Faster UI rendering
  • C. Automatic recreation of the Activity
  • D. A compile-time error
Correct answer: A. Holding an Activity Context past its lifecycle prevents GC, leaking the whole view hierarchy.
A ConstraintLayout view has width set to 0dp with start and end constrained to the parent. What does 0dp mean here?
  • A. Match constraints - stretch to fill between the constraints ✓
  • B. Zero width, invisible
  • C. Wrap content
  • D. Use the parent's exact pixel width
Correct answer: A. In ConstraintLayout 0dp means match_constraint, so the view expands to fill the constrained span.
You call `startActivityForResult` and it's deprecated. What replaces it in modern Android?
  • A. The Activity Result APIs (registerForActivityResult) ✓
  • B. A BroadcastReceiver
  • C. onActivityResult only
  • D. A bound Service
Correct answer: A. registerForActivityResult with an ActivityResultContract is the current, lifecycle-safe replacement.
Your release APK is much larger than expected and you want to strip unused code and resources. Which build feature helps?
  • A. Enabling R8/ProGuard minification and resource shrinking ✓
  • B. Switching to debug build type
  • C. Removing the manifest
  • D. Increasing minSdkVersion
Correct answer: A. R8 minification plus resource shrinking removes unused code and resources, reducing size.
A background Flow emits DB updates and you collect it in a Fragment. Which collector respects lifecycle to avoid wasted work while stopped?
  • A. repeatOnLifecycle(Lifecycle.State.STARTED) with launchWhenStarted-style collection ✓
  • B. A plain GlobalScope.launch collect
  • C. Collecting inside onCreate once
  • D. Collecting on a background thread with no scope
Correct answer: A. repeatOnLifecycle suspends collection when the UI is not at least STARTED, saving resources safely.
You define a build variant with a different applicationId suffix for debug. What does this allow?
  • A. Installing debug and release builds side by side on one device ✓
  • B. Faster network calls
  • C. Skipping signing
  • D. Automatic crash reporting
Correct answer: A. A distinct applicationId (suffix) makes it a separate package, so both builds coexist on the device.

Medium round 30 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+).
Why should you avoid holding a reference to an Activity in a long-lived singleton?
  • A. It slows down Gradle builds
  • B. It can cause a memory leak by preventing Activity garbage collection ✓
  • C. It disables ProGuard
  • D. It breaks XML inflation
Correct answer: B. A long-lived reference to an Activity keeps it from being garbage collected after it's destroyed, leaking memory.
In Kotlin coroutines, which dispatcher is appropriate for disk or network I/O?
  • A. Dispatchers.Main
  • B. Dispatchers.IO ✓
  • C. Dispatchers.Default
  • D. Dispatchers.Unconfined
Correct answer: B. Dispatchers.IO is optimized for blocking I/O work like disk and network operations.
In Jetpack Compose, what triggers recomposition of a composable?
  • A. Any change anywhere in the app
  • B. A change in a State object it reads ✓
  • C. A configuration file edit
  • D. Only a full Activity restart
Correct answer: B. Compose recomposes a composable when a State (observable) value it reads changes.
What is the main advantage of ViewModel surviving configuration changes?
  • A. It persists data to disk automatically
  • B. It retains UI-related state across rotations without reloading ✓
  • C. It encrypts network traffic
  • D. It replaces the need for a repository
Correct answer: B. A ViewModel outlives configuration changes like rotation, so UI state need not be reloaded.
When should you use WorkManager instead of a coroutine for background work?
  • A. For immediate UI-thread updates
  • B. For deferrable, guaranteed work that must survive process death/reboot ✓
  • C. For animating views
  • D. For parsing JSON in memory
Correct answer: B. WorkManager guarantees deferrable background work runs even across app restarts and device reboots.
In Room, what does a @Transaction annotation on a DAO method ensure?
  • A. Faster query compilation
  • B. The wrapped operations execute atomically ✓
  • C. Automatic pagination
  • D. Encryption of the database
Correct answer: B. @Transaction ensures the enclosed database operations run atomically as a single transaction.
Which is the correct way to update UI state from a coroutine doing background work?
  • A. Update views directly from Dispatchers.IO
  • B. Switch to Dispatchers.Main (or expose state via StateFlow) before touching UI ✓
  • C. Use a raw Thread and call setText
  • D. Post to a static field and hope
Correct answer: B. UI must be updated on the main thread, so switch to Dispatchers.Main or emit via StateFlow observed on the UI.
What problem does StateFlow solve compared to a plain Kotlin Flow for UI state?
  • A. It supports SQL queries
  • B. It is hot, holds a current value, and always emits the latest to new collectors ✓
  • C. It runs only on background threads
  • D. It replaces the need for a ViewModel
Correct answer: B. StateFlow is a hot, state-holding flow that always provides the current value to new collectors.
In the Navigation component, what is a 'safe args' plugin used for?
  • A. Encrypting the back stack
  • B. Type-safe passing of arguments between destinations ✓
  • C. Compressing navigation graphs
  • D. Auto-generating themes
Correct answer: B. Safe Args generates type-safe classes for passing arguments between navigation destinations.
Why prefer sealed classes for representing UI state (Loading/Success/Error)?
  • A. They run faster at runtime
  • B. They enable exhaustive when-expressions the compiler can check ✓
  • C. They reduce APK size
  • D. They allow multiple inheritance
Correct answer: B. Sealed classes let the compiler verify a when-expression handles every possible state exhaustively.
You perform a network call on the main thread and the app shows an ANR. What is the correct fix using coroutines?
  • A. Wrap the call in runBlocking on Dispatchers.Main
  • B. Launch the call in a coroutine on Dispatchers.IO and update UI on Dispatchers.Main ✓
  • C. Increase the ANR timeout in the manifest
  • D. Use a tight while-loop to retry on the main thread
Correct answer: B. Offloading blocking I/O to Dispatchers.IO keeps the main thread free, then results are posted back on the main dispatcher.
In the MVVM pattern with Android Architecture Components, what is the primary role of the ViewModel?
  • A. To render XML layouts directly
  • B. To hold and manage UI-related state, surviving configuration changes ✓
  • C. To perform SQL migrations
  • D. To replace the Activity lifecycle entirely
Correct answer: B. The ViewModel holds UI state and business logic and survives configuration changes like rotation.
Which coroutine scope is lifecycle-aware and automatically cancels when a ViewModel is cleared?
  • A. GlobalScope
  • B. viewModelScope ✓
  • C. runBlocking
  • D. CoroutineScope(Dispatchers.Main) created manually
Correct answer: B. viewModelScope is tied to the ViewModel lifecycle and cancels its coroutines when the ViewModel is cleared.
In Jetpack Compose, what happens when a State object read inside a composable changes?
  • A. Nothing until the app restarts
  • B. The reading composable is recomposed to reflect the new value ✓
  • C. The entire Activity is recreated
  • D. The change is ignored unless invalidate() is called manually
Correct answer: B. Compose tracks State reads and recomposes only the composables that read the changed state.
You need to run a deferrable, guaranteed background task that must survive app restarts and respect network constraints. Which API is recommended?
  • A. A raw Thread
  • B. WorkManager ✓
  • C. AsyncTask
  • D. A foreground Activity loop
Correct answer: B. WorkManager provides guaranteed, constraint-aware, persistable execution of deferrable background work.
In Room, why must you avoid running queries on the main thread by default?
  • A. Room queries are always slow regardless of thread
  • B. Blocking database I/O on the main thread can cause ANRs, so Room enforces suspend/async access ✓
  • C. Room does not support the main thread at all for any operation
  • D. The main thread cannot access SQLite files
Correct answer: B. Synchronous DB access blocks the UI thread risking ANRs, so Room requires suspend functions or async return types for main-thread safety.
A Fragment's view is destroyed but the Fragment instance is retained during navigation. Which observer owner should you use for LiveData to avoid leaks and stale UI updates?
  • A. The Fragment itself (this)
  • B. viewLifecycleOwner ✓
  • C. requireActivity()
  • D. A new LifecycleOwner created manually
Correct answer: B. Observing with viewLifecycleOwner ties observation to the view's lifecycle, preventing updates after the view is destroyed.
What is the main advantage of using StateFlow over LiveData for exposing UI state in modern Android?
  • A. StateFlow is Android-specific and lifecycle-bound automatically
  • B. StateFlow is a Kotlin-first, platform-agnostic hot flow that always has a value and integrates with coroutines/operators ✓
  • C. StateFlow cannot hold an initial value
  • D. StateFlow does not require any collector
Correct answer: B. StateFlow is a coroutine-native hot stream that always holds a current value and composes with flow operators, unlike LiveData.
Which technique reduces APK size by removing unused code and resources at build time?
  • A. Enabling multidex
  • B. R8 code shrinking and resource shrinking ✓
  • C. Increasing minSdkVersion
  • D. Disabling ProGuard entirely
Correct answer: B. R8 shrinks and optimizes bytecode while resource shrinking strips unused resources, reducing APK size.
In dependency injection with Hilt, which annotation marks a class the entry point for generated components at the application level?
  • A. @Inject
  • B. @HiltAndroidApp ✓
  • C. @Module
  • D. @Provides
Correct answer: B. @HiltAndroidApp on the Application class triggers Hilt's code generation and sets up the application-level component.

Hard round 30 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.
A LiveData observer keeps firing after the Fragment view is destroyed, causing crashes. The correct fix is:
  • A. Observe using the Fragment instance as lifecycle owner
  • B. Observe using viewLifecycleOwner in onViewCreated ✓
  • C. Remove all observers in onDestroy manually always
  • D. Switch LiveData to a plain callback
Correct answer: B. Using viewLifecycleOwner ties observation to the view lifecycle, avoiding stale observers after view destruction.
In Compose, wrapping expensive work in remember(key) recalculates only when:
  • A. Every recomposition regardless of key
  • B. The key changes between recompositions ✓
  • C. The Activity restarts
  • D. The app is backgrounded
Correct answer: B. remember(key) recomputes and caches its value only when the provided key changes across recompositions.
You call a suspend function from a ViewModel; when the user leaves the screen, the request should cancel automatically if launched in:
  • A. GlobalScope
  • B. viewModelScope ✓
  • C. A raw newSingleThreadExecutor
  • D. Dispatchers.IO directly without a scope
Correct answer: B. viewModelScope is cancelled when the ViewModel is cleared, automatically cancelling its coroutines.
Recomposition in Compose is skipped for a composable when its parameters are:
  • A. Mutable and unstable
  • B. Stable and unchanged (equals returns true) ✓
  • C. Always nullable
  • D. Wrapped in remember
Correct answer: B. Compose skips recomposition when a composable's inputs are stable and equal to the previous values.
An ANR is triggered when the main thread is blocked for roughly how long on input dispatch?
  • A. About 500 milliseconds
  • B. About 5 seconds ✓
  • C. About 20 seconds
  • D. About 60 seconds
Correct answer: B. Input-dispatch ANRs fire when the main thread fails to respond within about 5 seconds.
To ensure a cold Flow from Room stops collecting when the UI is not visible, you should collect it with:
  • A. collect { } in onCreate
  • B. repeatOnLifecycle(STARTED) / flowWithLifecycle ✓
  • C. GlobalScope.launch
  • D. LiveData.observeForever
Correct answer: B. repeatOnLifecycle(STARTED) suspends and cancels collection as the lifecycle drops below STARTED, saving resources.
Two coroutines update shared mutable state causing a race. The idiomatic Kotlin fix is:
  • A. Add @Synchronized to a suspend function
  • B. Confine updates via a Mutex or a single-threaded/actor context ✓
  • C. Use volatile on the coroutine
  • D. Switch to Dispatchers.Unconfined
Correct answer: B. A Mutex or single-threaded confinement serializes access to shared state, preventing the race safely with suspension.
With R8/ProGuard enabled, a reflection-based library crashes in release only. The correct remedy is:
  • A. Disable minification for the whole app
  • B. Add keep rules for the reflected classes/members ✓
  • C. Rename the classes to shorter names
  • D. Move code to a different package
Correct answer: B. R8 strips/renames unused or reflected symbols; -keep rules preserve classes accessed via reflection at runtime.
Passing a large Bitmap between Activities via Intent extras risks:
  • A. A NullPointerException always
  • B. TransactionTooLargeException due to Binder buffer limits ✓
  • C. Slower Gradle sync
  • D. A ClassNotFoundException
Correct answer: B. The Binder transaction buffer (~1MB) is limited; large payloads in extras throw TransactionTooLargeException.
In Compose, deriving a value from other state without extra recompositions is best done with:
  • A. mutableStateOf on every read
  • B. derivedStateOf ✓
  • C. LaunchedEffect(Unit)
  • D. remember without a key
Correct answer: B. derivedStateOf recomputes only when its underlying reads change, avoiding unnecessary recompositions of readers.
In Jetpack Compose, a lambda inside a frequently-recomposing composable is created anew each recomposition, breaking skipping of a child. Which approach preserves stability and avoids unnecessary recomposition?
  • A. Wrapping the whole screen in a single composable
  • B. Using remember (or referencing a stable method reference) so the lambda identity is preserved ✓
  • C. Marking the child as @Composable twice
  • D. Calling recompose() manually
Correct answer: B. Remembering the lambda (or using a stable reference) keeps a consistent instance so Compose can skip the unchanged child.
A background coroutine started in viewModelScope must NOT be cancelled when the user navigates away mid-write to the database. What is the correct pattern?
  • A. Run it in GlobalScope so it never cancels
  • B. Wrap the critical section in withContext(NonCancellable) ✓
  • C. Use runBlocking on the main thread
  • D. Catch CancellationException and swallow it
Correct answer: B. withContext(NonCancellable) protects a critical suspend section from cancellation while keeping normal scope semantics elsewhere.
Your app leaks memory because an inner AsyncTask/handler holds an implicit reference to the destroyed Activity. What is the root cause and correct fix?
  • A. The Activity is static; make it non-static
  • B. A non-static inner class holds an implicit outer Activity reference; use a static/nested class with a WeakReference or lifecycle-aware scope ✓
  • C. Garbage collection is disabled; enable it
  • D. The Activity uses too many views; reduce them
Correct answer: B. Non-static inner classes capture the enclosing Activity; using a static class with a WeakReference (or lifecycle-scoped work) avoids the leak.
In Compose, you observe janky scrolling in a LazyColumn showing thousands of items. Which combination most improves performance?
  • A. Providing stable keys via the key parameter and hoisting heavy work out of the item composable ✓
  • B. Wrapping each item in its own remember { } with random keys
  • C. Replacing LazyColumn with a Column inside a verticalScroll
  • D. Recomposing the entire list on every scroll frame
Correct answer: A. Stable item keys let Compose reuse/skip items correctly, and moving heavy computation out of item scope keeps frames fast; a scrollable Column would compose all items eagerly.
You need cold-start latency reduction for an app with heavy initialization. Which App Startup / initialization strategy is most correct?
  • A. Do all initialization synchronously in Application.onCreate()
  • B. Use lazy initialization and the App Startup library to defer/parallelize non-critical initializers off the critical path ✓
  • C. Move everything to a ContentProvider so it runs earlier and blocks startup
  • D. Initialize on the main thread inside the launcher Activity's onResume repeatedly
Correct answer: B. Deferring and parallelizing non-critical initializers (e.g., via App Startup and lazy init) shortens the critical startup path.
A Flow collected in the UI keeps running when the app goes to the background, wasting resources. Which operator/API safely stops collection when the lifecycle is stopped and restarts it later?
  • A. flowOn(Dispatchers.Main)
  • B. repeatOnLifecycle(Lifecycle.State.STARTED) around the collect ✓
  • C. buffer() with unlimited capacity
  • D. conflate() on the flow
Correct answer: B. repeatOnLifecycle(STARTED) cancels collection when the lifecycle drops below STARTED and re-launches it when it returns.
Two coroutines update shared mutable state concurrently, causing a race. Which is the most idiomatic Kotlin coroutine solution that avoids blocking?
  • A. Use synchronized(this) blocks around each update
  • B. Use a Mutex with withLock, or confine state to a single-threaded dispatcher/actor ✓
  • C. Use Thread.sleep to space out updates
  • D. Mark the variable @Volatile only
Correct answer: B. A coroutine Mutex.withLock (or confining mutations to a single dispatcher) provides non-blocking mutual exclusion; @Volatile alone doesn't make compound updates atomic.
In a ViewModel exposing UI state, you must ensure that a one-time event (e.g., a navigation or snackbar) is not re-emitted after a configuration change re-subscribes the collector. What is the correct approach?
  • A. Expose it as StateFlow with the event as the value
  • B. Model events with a Channel/SharedFlow (replay 0) or a consumed one-shot event wrapper ✓
  • C. Store the event in a static variable
  • D. Emit it from LiveData with postValue repeatedly
Correct answer: B. One-time events need a hot stream with no replay (Channel or SharedFlow replay=0) or a consumed-event wrapper so they fire once, not on re-subscription.
Your Room migration adds a NOT NULL column with no default to an existing table and the app crashes on upgrade for existing users. What is the correct migration handling?
  • A. Set fallbackToDestructiveMigration for production without warning
  • B. Provide an explicit Migration that ALTERs the table adding the column with a default or backfill for existing rows ✓
  • C. Bump the version number only and let Room auto-handle it
  • D. Delete the database file on every launch
Correct answer: B. Adding a NOT NULL column to existing rows requires an explicit migration supplying a default/backfill; destructive migration would wipe user data.
You must securely store an authentication token on-device. Which approach best resists extraction on a rooted device?
  • A. Plain SharedPreferences
  • B. Encrypting via a key stored in the Android Keystore (hardware-backed when available) ✓
  • C. Hardcoding it in the source and obfuscating with R8
  • D. Storing it Base64-encoded in a local file
Correct answer: B. The Android Keystore keeps key material in hardware-backed secure storage that is not directly extractable, unlike plaintext or reversible encodings.

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: August 2026.