Skip to content
RED SAGSkills

Android interview questions and answers

Android interview questions cover the app lifecycle, UI and background work on real devices.

Below are 60 Android questions with the correct answer and a short explanation, grouped from fresher to experienced level. Try each one before you open the answer.

What Android interviews usually check

  • ✓Activity and fragment lifecycle
  • ✓Jetpack Compose and views
  • ✓ViewModel, state and configuration changes
  • ✓Background work and WorkManager
  • ✓Performance and memory leaks

Android interview questions for freshers easy

1. Kotlin data class equality

What does this Kotlin code print?

data class User(val id: Int, val name: String)
val a = User(1, "Ana")
val b = User(1, "Ana")
println(a == b)
println(a === b)
  1. A.true then false
  2. B.true then true
  3. C.false then false
  4. D.false then true
Show answer

Answer: A. true then false

A data class generates equals() based on its constructor properties, so == (structural equality) is true. === checks whether both variables point to the same object, and two separate constructor calls create two objects, so it is false.

2. Sharing text to any app

On Android, you want to let the user share a piece of text using any app they choose, such as WhatsApp or Gmail. What do you use?

  1. A.An explicit Intent for each popular app, such as WhatsApp and Gmail, tried one after another until one starts
  2. B.A BroadcastReceiver registered in the manifest
  3. C.An implicit Intent with ACTION_SEND, shown with Intent.createChooser
  4. D.A ContentProvider that other apps can query
Show answer

Answer: C. An implicit Intent with ACTION_SEND, shown with Intent.createChooser

An implicit intent describes an action (ACTION_SEND with a text type) and lets the system find every app that can handle it. createChooser shows the share sheet so the user picks. An explicit intent only targets one specific app, which may not even be installed.

3. Dart null-aware operators

What does this Dart code print?

String? name;
print(name ?? 'Guest');
print(name?.length);
  1. A.Guest, then 0
  2. B.A null check error is thrown
  3. C.Guest, then null
  4. D.null, then null
Show answer

Answer: C. Guest, then null

name is null, so ?? returns the fallback 'Guest'. The ?. operator short-circuits on null and returns null instead of throwing, so the second line prints null. Only the ! operator would throw on a null value.

4. What !! does on null

What happens when this line runs and token is null?

val token: String? = prefs.getString("token", null)
val length = token!!.length
  1. A.length becomes 0
  2. B.length becomes null
  3. C.A NullPointerException is thrown
  4. D.The line is skipped silently
Show answer

Answer: C. A NullPointerException is thrown

The not-null assertion !! converts a nullable value to non-null and throws NullPointerException if it is null. It should be rare in app code; prefer ?., ?: or an explicit check with a clear error.

5. What let returns

What is the type of len?

val name: String? = intent.getStringExtra("name")
val len = name?.let { it.length }
  1. A.String?
  2. B.Unit
  3. C.Int
  4. D.Int?
Show answer

Answer: D. Int?

let passes the receiver as it and returns the lambda’s last expression, here it.length (Int). Because the call is name?.let, the result is null when name is null, so the type is Int?.

6. Configuring an object with apply

What does this expression return?

val paint = Paint().apply {
    color = Color.RED
    strokeWidth = 4f
    style = Paint.Style.STROKE
}
  1. A.The configured Paint object
  2. B.Unit, because the lambda sets properties
  3. C.The last value set, Paint.Style.STROKE
  4. D.A copy of a new default Paint
Show answer

Answer: A. The configured Paint object

apply runs the block with the object as this and returns the object itself. That makes it handy for builder-style setup of views, paints or intents in one expression.

7. == versus === in Kotlin

For two different String objects with the same text, what do a == b and a === b return?

  1. A.false and true
  2. B.true and true
  3. C.true and false
  4. D.false and false
Show answer

Answer: C. true and false

== checks structural equality by calling equals(). === checks referential identity, whether both point to the same object. Two distinct objects with equal content give true for == and false for ===.

8. A click callback type

A composable takes a callback that receives the clicked item’s id (Int) and returns nothing. What is the parameter type?

  1. A.Int -> Void
  2. B.() -> Int
  3. C.Function<Int>
  4. D.(Int) -> Unit
Show answer

Answer: D. (Int) -> Unit

Kotlin function types are written (parameters) -> return type. Unit means no meaningful return value. A caller can pass a lambda such as { id -> viewModel.open(id) }.

9. Two suspend calls in a row

getProfile() takes 1.0 s and getOrders() takes 1.5 s. Called one after the other in the same coroutine, how long does it take?

  1. A.About 1.5 s
  2. B.About 2.5 s
  3. C.About 1.0 s
  4. D.About 0.5 s
Show answer

Answer: B. About 2.5 s

Suspend calls in a coroutine run sequentially by default, just like normal code. Each waits for the previous one, so times add up. Use async inside coroutineScope when the calls are independent.

10. Launching in GlobalScope

Why is GlobalScope.launch discouraged for loading screen data?

  1. A.It runs every coroutine on the main thread only
  2. B.It is not able to call any suspend functions
  3. C.The work is tied to no lifecycle and can leak
  4. D.It always crashes on devices with Android 14
Show answer

Answer: C. The work is tied to no lifecycle and can leak

GlobalScope coroutines live as long as the app process and are not cancelled when a screen closes. This leaks work and memory. Use viewModelScope, lifecycleScope or an injected application scope for truly app-wide work.

11. Making a composable reusable

A QuantityPicker composable keeps its own count with remember. The cart screen needs to read and reset that count. What is the standard fix?

  1. A.Make count a global top-level var in the file
  2. B.Read the count from the composable using reflection
  3. C.Hoist it: pass count in and expose an onCountChange callback
  4. D.Store the count in a companion object of the screen
Show answer

Answer: C. Hoist it: pass count in and expose an onCountChange callback

State hoisting moves state to the caller and makes the composable stateless: it receives the value and reports changes through a lambda. The caller now owns the single source of truth and can read, reset or save it.

12. Counter jumps back to zero

The count sometimes jumps back to 0, for example when the parent screen recomposes. Why?

@Composable
fun Counter() {
    var count by mutableStateOf(0)
    Button(onClick = { count++ }) {
        Text("Count: $count")
    }
}
  1. A.mutableStateOf only works inside a ViewModel class
  2. B.Button onClick runs on a background thread instead
  3. C.Text cannot display numbers that come from state
  4. D.A new state is made each time Counter runs; use remember
Show answer

Answer: D. A new state is made each time Counter runs; use remember

Every time Counter itself recomposes, the function body runs again and creates a fresh mutableStateOf(0), losing the old value. remember stores the state object in the composition so the same one is reused across recompositions; Android lint also flags state created without remember.

13. Showing 5,000 orders

A screen shows 5,000 order rows using Column with verticalScroll and is very slow to open. What should it use?

  1. A.LazyColumn, which composes only visible items
  2. B.A Box with 5,000 children stacked
  3. C.A Row with horizontalScroll instead
  4. D.A Column inside another Column
Show answer

Answer: A. LazyColumn, which composes only visible items

Column composes and measures every child at once. LazyColumn composes only the items on screen plus a small buffer, and reuses work as the user scrolls, like RecyclerView did for Views.

14. Auto-incrementing IDs in Room

You want Room to assign IDs automatically for new Note rows. What do you write?

  1. A.@Entity(autoId = true) on the class
  2. B.@ColumnInfo(name = "auto") on the id field
  3. C.@Index(unique = true) on the id field
  4. D.@PrimaryKey(autoGenerate = true) val id: Long = 0
Show answer

Answer: D. @PrimaryKey(autoGenerate = true) val id: Long = 0

With autoGenerate = true, inserting an entity whose id is 0 lets SQLite assign the next row ID. The insert DAO method can return the new Long ID.

15. Room query in onCreate

A plain (non-suspend) Room DAO query is called from onCreate on the main thread. What happens by default?

  1. A.The query runs and blocks the UI
  2. B.Room moves it to a background thread
  3. C.The query returns an empty list
  4. D.Room throws an IllegalStateException
Show answer

Answer: D. Room throws an IllegalStateException

Room refuses main-thread database access unless allowMainThreadQueries() is set, because disk I/O can freeze the UI. Use suspend DAO functions or Flow return types, which run off the main thread.

16. App Not Responding dialog

When does Android typically show an ANR for a foreground activity?

  1. A.The app uses more than half of the device CPU for a while
  2. B.Any single network call takes more than one second
  3. C.The main thread misses an input event for about 5 seconds
  4. D.The screen stays on for more than five minutes in a row
Show answer

Answer: C. The main thread misses an input event for about 5 seconds

An ANR fires when the main thread is blocked and cannot respond to input for about 5 seconds. Long disk, network or CPU work on the main thread is the usual cause. Android vitals in Play Console tracks ANR rate.

17. Sync every 5 minutes

You schedule a PeriodicWorkRequest with a 5-minute interval. What does WorkManager do?

  1. A.Runs it every 5 minutes exactly
  2. B.Uses the 15-minute minimum interval instead
  3. C.Throws an error and never schedules it
  4. D.Runs it once and then stops
Show answer

Answer: B. Uses the 15-minute minimum interval instead

Periodic work cannot run more often than every 15 minutes, and WorkManager raises shorter intervals to that minimum. Exact timing is also not guaranteed because of Doze and battery rules. Use push or a foreground service for near-real-time needs.

18. Blocking screenshots of sensitive screens

A banking app wants to stop screenshots and screen recordings of its card details screen on Android. What does it use?

  1. A.A transparent overlay drawn on the screen
  2. B.The android:allowBackup flag in the manifest
  3. C.A lower screen brightness while it is shown
  4. D.WindowManager.LayoutParams.FLAG_SECURE on that window
Show answer

Answer: D. WindowManager.LayoutParams.FLAG_SECURE on that window

FLAG_SECURE tells the system to treat the window as secure: screenshots and recordings show black, and it is hidden in the recent apps preview. Apply it only to sensitive screens so normal usage like sharing is not blocked.

19. Copying a one-time password

An app copies an OTP to the clipboard for the user. What is a good practice on modern Android?

  1. A.Keep the OTP in the clipboard permanently
  2. B.Copy the user's password along with the OTP
  3. C.Paste the OTP into every text field automatically
  4. D.Mark the clip as sensitive so previews hide it
Show answer

Answer: D. Mark the clip as sensitive so previews hide it

Other apps and keyboards may read clipboard data, and Android 13 shows a visual preview of copied content. Setting the EXTRA_IS_SENSITIVE flag hides it from the preview. Avoid copying secrets where possible, for example by using SMS Retriever or autofill instead.

20. viewDidLoad vs viewWillAppear

A UIKit screen should refresh its data every time the user comes back to it from a pushed screen. Where should the refresh go?

  1. A.viewDidLoad() once
  2. B.viewWillAppear(_:)
  3. C.init(coder:) on load
  4. D.loadView() override
Show answer

Answer: B. viewWillAppear(_:)

viewDidLoad runs once when the view is first loaded into memory. viewWillAppear runs each time the view is about to become visible, including when popping back to it.

Android intermediate interview questions medium

21. remember vs rememberSaveable

In Jetpack Compose, a text field value is lost when the screen rotates. It is stored with remember { mutableStateOf("") }. What should you use instead?

  1. A.derivedStateOf
  2. B.LaunchedEffect
  3. C.rememberSaveable { mutableStateOf("") }
  4. D.A plain var outside the composable
Show answer

Answer: C. rememberSaveable { mutableStateOf("") }

remember keeps a value across recompositions but not across Activity recreation. rememberSaveable saves the value into the saved instance state Bundle, so it survives rotation and even process death for simple types. Larger screen state usually belongs in a ViewModel.

22. Reliable background upload

An Android app must upload photos even if the user closes the app, but only on Wi-Fi. Which API should you use?

  1. A.A Thread started from the Activity
  2. B.WorkManager with a network-type constraint of UNMETERED
  3. C.A coroutine in viewModelScope
  4. D.An AlarmManager that fires every minute
Show answer

Answer: B. WorkManager with a network-type constraint of UNMETERED

WorkManager is meant for deferrable work that must run reliably, even after the app is closed or the device restarts. It supports constraints like unmetered network and charging, and retries with backoff. A thread or viewModelScope coroutine dies with the process or screen.

23. LaunchedEffect keyed on Unit

The parent changes userId from "a" to "b" while this Profile composable stays on screen. What happens?

@Composable
fun Profile(userId: String, vm: ProfileViewModel) {
  LaunchedEffect(Unit) {
    vm.load(userId)
  }
  // ... UI
}
  1. A.load("b") runs, because the composable recomposed
  2. B.Nothing new loads, because an effect keyed on Unit runs only once; use LaunchedEffect(userId) so it restarts when the id changes
  3. C.The app crashes with an IllegalStateException, because an effect key cannot stay the same while the parameters of its composable change
  4. D.load runs twice in parallel, once for each id
Show answer

Answer: B. Nothing new loads, because an effect keyed on Unit runs only once; use LaunchedEffect(userId) so it restarts when the id changes

LaunchedEffect starts its coroutine when it enters the composition and restarts only when one of its keys changes. Unit never changes, so recomposition with a new userId does not run load again. Passing userId as the key cancels the old coroutine and starts a new one for the new id.

24. Release build breaks JSON parsing

The debug build works, but the release build crashes or returns empty objects when parsing JSON with a reflection-based library such as Gson. What is the likely cause?

  1. A.Release builds do not have the INTERNET permission
  2. B.R8 renamed or removed model classes and fields; add keep rules or use a code-generating serializer like kotlinx.serialization or Moshi codegen
  3. C.JSON parsing is turned off in release builds
  4. D.The server detects release builds from the user agent and sends a compact JSON format with short keys, so every parsed field ends up empty
Show answer

Answer: B. R8 renamed or removed model classes and fields; add keep rules or use a code-generating serializer like kotlinx.serialization or Moshi codegen

R8 shrinks and obfuscates release builds, renaming fields like userName to a. Reflection-based parsers look fields up by name at runtime, so they no longer match the JSON keys. Keep rules, @SerializedName annotations or compile-time serializers fix this, and release builds should always be tested before shipping.

25. also compared with apply

How does also differ from apply?

  1. A.also returns the lambda result; apply returns Unit
  2. B.also runs on a background thread; apply does not
  3. C.also gives it, apply gives this; both return the object
  4. D.also only works on nullable values; apply does not
Show answer

Answer: C. also gives it, apply gives this; both return the object

Both return the receiver, so they chain well. apply gives this, which suits setting properties. also gives it, which reads better for side effects such as logging or validation: user.also { log(it) }.

26. Snackbar shows again after rotation

A ViewModel exposes a StateFlow<String?> message. After rotation, the same Snackbar appears again. Why?

  1. A.Rotation re-runs the ViewModel’s init block again
  2. B.Snackbars are always shown again after rotation
  3. C.collect() reads the latest value twice per frame
  4. D.StateFlow hands its current value to new collectors
Show answer

Answer: D. StateFlow hands its current value to new collectors

The new screen collects again and immediately receives the latest value. Either mark the message as shown by clearing it in state after display, or deliver one-off events through a Channel received once.

27. When combine first emits

You combine a user flow and a settings flow. The user flow emits at once, but settings emits after 2 seconds. When does combine first emit?

  1. A.Immediately, with settings as null
  2. B.Never, unless both emit at the same time
  3. C.Only after the user flow emits twice
  4. D.After 2 seconds, once both have a value
Show answer

Answer: D. After 2 seconds, once both have a value

combine needs a latest value from each source before it can call the transform. After that, it emits whenever any source emits, using the latest values of the others.

28. Channel vs SharedFlow

Two coroutines receive from the same Channel. Five values are sent. What happens?

  1. A.Both receivers get all five values
  2. B.The second receiver throws an exception
  3. C.Each value goes to only one of the receivers
  4. D.Values are dropped until a third receiver joins
Show answer

Answer: C. Each value goes to only one of the receivers

A Channel is a queue: each element is received by exactly one consumer, which spreads work (fan-out). A SharedFlow broadcasts each emission to all current collectors.

29. Value stuck after the id changes

A composable computes a formatted report with remember { format(order) }. When order changes, the old text stays. What fixes it?

  1. A.rememberSaveable { format(order) }
  2. B.LaunchedEffect(Unit) { format(order) }
  3. C.remember(order) { format(order) }
  4. D.derivedStateOf without remember
Show answer

Answer: C. remember(order) { format(order) }

remember without keys keeps the first value for the life of the composition. Passing order as a key makes Compose recompute the value whenever the key changes.

30. Mixed item types in a LazyColumn

A feed LazyColumn mixes headers, ads and posts. What does setting contentType on items help with?

  1. A.Sorting the feed items by their declared type
  2. B.Reusing compositions only among items of one type
  3. C.Loading each type of item from the network
  4. D.Making each item type scroll on its own
Show answer

Answer: B. Reusing compositions only among items of one type

Lazy lists recycle compositions as items scroll. With contentType, Compose reuses a post’s composition only for another post, not an ad, which reduces work when item layouts differ.

31. Launching from a click handler

On button click you want to call a suspend function that scrolls a list. Why can’t you use LaunchedEffect inside onClick, and what do you use?

  1. A.LaunchedEffect only runs on IO; use GlobalScope.launch
  2. B.Suspend calls are not allowed in Compose at all
  3. C.onClick is already a coroutine, so just call it
  4. D.LaunchedEffect is composable-only; use rememberCoroutineScope().launch
Show answer

Answer: D. LaunchedEffect is composable-only; use rememberCoroutineScope().launch

Effects like LaunchedEffect can only be called during composition, and onClick is a normal callback. rememberCoroutineScope gives a scope tied to the composition, so the launched coroutine is cancelled when it leaves.

32. Clicking a button in a Compose test

In a Compose UI test, how do you find the "Pay" button reliably and click it, even if its label is translated?

  1. A.Use Espresso onView(withId(R.id.pay)).perform(click())
  2. B.Find it by its x and y screen position and tap there
  3. C.Set testTag("pay"), then onNodeWithTag("pay").performClick()
  4. D.Call the button’s onClick lambda directly by reflection
Show answer

Answer: C. Set testTag("pay"), then onNodeWithTag("pay").performClick()

Compose tests query the semantics tree. A testTag is a stable identifier that does not change with language or copy edits. createComposeRule or createAndroidComposeRule provides onNodeWithTag, performClick and assertions like assertIsDisplayed.

33. Handling the Android back button

A full-screen photo editor should ask "Discard changes?" when the Android hardware back button is pressed. Which API lets you intercept it?

  1. A.Keyboard.addListener with the hardwareBack event name
  2. B.AppState change events with the back state
  3. C.Linking.addEventListener for the back URL
  4. D.BackHandler, returning true from the listener to stop the default
Show answer

Answer: D. BackHandler, returning true from the listener to stop the default

BackHandler lets JS subscribe to the hardware back press. Returning true means you handled it; false lets the default (pop or exit) happen. Remove the listener on blur or unmount, and in React Navigation the beforeRemove event covers both back gestures and buttons.

34. What triggers an ANR

An Android app shows "App isn't responding". What is the typical trigger for an input-dispatch ANR?

  1. A.A background thread runs for more than one full minute
  2. B.The app uses more than 100 MB of memory
  3. C.A network call returns an HTTP 500 error
  4. D.The main thread does not handle input for about 5 seconds
Show answer

Answer: D. The main thread does not handle input for about 5 seconds

Android raises an ANR when the main thread cannot respond to an input event within about 5 seconds, or when a broadcast receiver or service takes too long. Common causes are disk or network I/O, heavy parsing or lock waits on the main thread. Move such work to background threads or coroutines.

35. Layouts for tablets and foldables

An app must work on phones, foldables and tablets. What do Android's window size classes help with?

  1. A.Deciding how much RAM to request from the system
  2. B.Choosing layouts by available window width, not device type
  3. C.Setting a different app icon for each screen size
  4. D.Locking the app to portrait mode on large screens
Show answer

Answer: B. Choosing layouts by available window width, not device type

Window size classes (compact, medium, expanded) group the current window width and height into breakpoints. A list-detail screen can show one pane when compact and two panes when expanded. Using window size handles split-screen and folding, where device type alone would be wrong.

36. Which app process is killed first

Memory is low on an Android phone. Which process is the system most likely to kill first?

  1. A.A cached app that the user left a while ago
  2. B.The app currently in the foreground
  3. C.An app running a visible foreground service
  4. D.The system UI that draws the status bar
Show answer

Answer: A. A cached app that the user left a while ago

Android ranks processes by importance: foreground, visible, service, then cached. Cached background processes are killed first to free memory. That is why apps must save state and expect to be restarted from scratch when the user returns.

37. User grants only approximate location

On Android 12+, an app requests ACCESS_FINE_LOCATION and ACCESS_COARSE_LOCATION. The user picks "Approximate". What should the app expect?

  1. A.Only coarse location is granted, so the app must cope with it
  2. B.The request fails and no location at all is granted to it
  3. C.Fine location is granted anyway after 24 hours
  4. D.The app is forced to close by the system
Show answer

Answer: A. Only coarse location is granted, so the app must cope with it

Android 12 lets users choose approximate or precise location. If they pick approximate, only COARSE is granted, accurate to roughly a few kilometres. Features like nearby store lists should degrade gracefully; ask for precise only when a feature truly needs it.

38. Cleartext HTTP fails on Android

A new Android app calls an HTTP (not HTTPS) test server and gets "Cleartext HTTP traffic not permitted". Why?

  1. A.Android blocks all network traffic in debug builds
  2. B.Apps targeting API 28+ block cleartext traffic by default
  3. C.HTTP only works on Android when the phone is on Wi-Fi
  4. D.The INTERNET permission must be requested at runtime
Show answer

Answer: B. Apps targeting API 28+ block cleartext traffic by default

Since Android 9 (API 28), usesCleartextTraffic defaults to false. A network security config can allow cleartext for specific debug hosts, such as a local test server, without opening it for production domains.

39. Obfuscated Android crash traces

Crash reports from a release Android build show names like `a.b.c.d(Unknown Source)`. What makes them readable?

  1. A.Uploading the R8 mapping.txt for that build to the crash tool
  2. B.Turning off minification in the next release only
  3. C.Asking users to send screenshots of each crash
  4. D.Building the release on a faster CI machine
Show answer

Answer: A. Uploading the R8 mapping.txt for that build to the crash tool

R8 renames classes and methods and writes mapping.txt so the names can be restored. Crash tools like Crashlytics and Play Console use the mapping file of the exact build to deobfuscate traces. The Crashlytics Gradle plugin can upload it automatically.

40. Data loaded again and again

A tab’s .onAppear starts a network load. Users report it reloads each time they switch tabs. Why?

  1. A.onAppear runs each time the view appears, not once
  2. B.onAppear runs on every frame of animation
  3. C.TabView recreates the app on each switch
  4. D.Network calls repeat automatically in SwiftUI
Show answer

Answer: A. onAppear runs each time the view appears, not once

onAppear fires whenever the view comes on screen, such as tab switches or popping back. Keep loaded data in a model owned above the view, and load once or only when stale, often with .task.

Android interview questions for experienced candidates hard

41. Testing process death

A checkout screen works after rotation but loses the cart when the user returns after a long time in another app. What is happening and how do you fix it?

  1. A.The ViewModel was garbage collected because of a memory leak; add more RAM
  2. B.The OS killed the backgrounded process; the ViewModel was lost, so save key state with SavedStateHandle or persistent storage and restore it
  3. C.The user cleared the app cache manually; nothing can be done
  4. D.Rotation and backgrounding are the same, so the bug must be on the server
Show answer

Answer: B. The OS killed the backgrounded process; the ViewModel was lost, so save key state with SavedStateHandle or persistent storage and restore it

ViewModels survive configuration changes but not process death. Android may kill a background process to free memory, then recreate the screen when the user returns. Small UI state should go into SavedStateHandle, and important data like a cart should be persisted to a database. Test with the "Don't keep activities" option or adb shell am kill.

42. Slow cold start

An Android app takes 3 seconds to show its first screen on a cold start. Profiling shows many SDKs initialised in Application.onCreate. What is the most effective improvement?

  1. A.Defer or lazily initialise non-critical SDKs, move work off the main thread, and add a Baseline Profile
  2. B.Show a longer splash screen animation
  3. C.Increase minSdk to the latest version
  4. D.Move all code into one large Activity
Show answer

Answer: A. Defer or lazily initialise non-critical SDKs, move work off the main thread, and add a Baseline Profile

Everything in Application.onCreate runs on the main thread before the first frame. Analytics, ads and similar SDKs can usually start later or in the background, for example with App Startup or lazy init. Baseline Profiles let ART precompile hot code paths, which commonly cuts startup time noticeably. Measure with Macrobenchmark.

43. Collecting a cold Flow twice

What does this Kotlin program print, one value per line?

val numbers = flow {
  println("start")
  emit(1)
  emit(2)
}

fun main() = runBlocking {
  numbers.collect { println(it) }
  numbers.take(1).collect { println(it) }
}
  1. A.start, 1, 2, 1
  2. B.start, 1, 2, start, 1, 2
  3. C.1, 2, 1
  4. D.start, 1, 2, start, 1
Show answer

Answer: D. start, 1, 2, start, 1

A flow built with flow { } is cold: its block runs again from the start for every collector, so "start" prints twice. take(1) cancels the upstream right after the first value, so emit(2) never runs the second time. This is why expensive cold flows shared by several collectors are often converted with stateIn or shareIn.

44. PendingIntent crash on Android 12

After targeting Android 12 (API 31) or higher, creating a PendingIntent with flags set to 0 crashes the app. Why?

  1. A.PendingIntent was removed in Android 12
  2. B.The app needs the SCHEDULE_EXACT_ALARM permission for every PendingIntent it creates, including ones used for notifications and widgets
  3. C.You must explicitly pass FLAG_IMMUTABLE or FLAG_MUTABLE; prefer FLAG_IMMUTABLE unless another app must fill in parts of the intent
  4. D.PendingIntent flags must be negative numbers
Show answer

Answer: C. You must explicitly pass FLAG_IMMUTABLE or FLAG_MUTABLE; prefer FLAG_IMMUTABLE unless another app must fill in parts of the intent

From API 31, apps must declare whether each PendingIntent is mutable, and leaving it unspecified throws an IllegalArgumentException. Immutable is safer because the receiving app cannot change the wrapped intent. Mutable is only needed for cases like inline reply or bubbles where the system adds data.

45. Foreground service fails on Android 14

Your app targets Android 14 (API 34). Starting a foreground service that tracks a running workout now throws an exception. What is required?

  1. A.Declare a foregroundServiceType for the service in the manifest (such as health or location) and request the matching FOREGROUND_SERVICE_* permission
  2. B.Nothing can be done; foreground services are banned on Android 14
  3. C.Replace the service with a manifest BroadcastReceiver, because Android 14 only allows long-running work such as workout tracking inside receivers
  4. D.Show a Toast before calling startForeground
Show answer

Answer: A. Declare a foregroundServiceType for the service in the manifest (such as health or location) and request the matching FOREGROUND_SERVICE_* permission

Apps targeting Android 14 must declare at least one foreground service type for each foreground service, and each type has its own permission. Missing it throws an exception when the service is started. Types make the purpose explicit and let Google Play review whether the service is justified.

46. Exported activity loading any URL

An exported Android activity reads a "url" extra from its intent and loads it in a WebView with JavaScript enabled and a JavaScript interface to native code. What is the risk?

  1. A.None; exported only controls whether the activity gets a launcher icon, and other apps can never start an activity in a different app with their own extras
  2. B.Only slower page loads
  3. C.Exported only matters on rooted phones
  4. D.Any installed app can launch it with a malicious URL that then calls the native bridge; validate the URL against an allowlist, or set exported to false if outside apps do not need it
Show answer

Answer: D. Any installed app can launch it with a malicious URL that then calls the native bridge; validate the URL against an allowlist, or set exported to false if outside apps do not need it

An exported component can be started by any app on the device with any extras it chooses. Loading untrusted URLs into a WebView that exposes native methods lets attacker pages run code with your app permissions. Keep components unexported by default and treat all incoming intent data as untrusted input.

47. FCM handler skipped in background

You send FCM messages with a "notification" block plus data. When the Android app is in the background, onMessageReceived is not called and your custom handling never runs. Why?

  1. A.FCM is disabled for apps in the background
  2. B.Background delivery needs the RECEIVE_BOOT_COMPLETED permission, and without it FCM drops every message for apps that are not in the foreground
  3. C.In the background, the system itself shows messages that have a notification block and skips onMessageReceived; send data-only messages if the app must handle them
  4. D.onMessageReceived only works in debug builds
Show answer

Answer: C. In the background, the system itself shows messages that have a notification block and skips onMessageReceived; send data-only messages if the app must handle them

FCM treats messages with a notification payload as display messages: in the background the SDK posts them to the system tray automatically. onMessageReceived runs for them only in the foreground, while data-only messages reach it in both states. Data-only messages still have delivery limits, so keep their handling short.

48. Data class holding an array

Why does Photo(1, byteArrayOf(1, 2)) == Photo(1, byteArrayOf(1, 2)) return false?

  1. A.Data classes are not allowed to hold arrays
  2. B.Generated equals() compares arrays by reference
  3. C.The first property is compared as a string
  4. D.byteArrayOf fills arrays with random contents
Show answer

Answer: B. Generated equals() compares arrays by reference

Generated equals() calls equals() on each property, and arrays inherit identity equality from Object. Two arrays with the same contents are still different objects. Use List<Byte>, or override equals and hashCode with contentEquals and contentHashCode.

49. Handler on a child coroutine

A crash still happens even though the inner launch has a CoroutineExceptionHandler. Why?

scope.launch {
    launch(CoroutineExceptionHandler { _, e -> log(e) }) {
        throw IOException("boom")
    }
}
  1. A.CoroutineExceptionHandler works only with async builders
  2. B.The handler must be the first element in every context
  3. C.Handlers are always ignored when running on Dispatchers.IO
  4. D.Children pass errors to the parent; handlers work at the root
Show answer

Answer: D. Children pass errors to the parent; handlers work at the root

In structured concurrency, a failing child propagates its exception up to its parent. Only a handler on the root coroutine (or the scope) is used for uncaught exceptions. Put it in the scope, or catch the error inside the child.

50. Child list never skips

OrderList recomposes every time the parent does, even when orders did not change. Why?

@Composable
fun OrdersScreen(orders: List<Order>, query: String) {
    SearchBar(query)
    OrderList(orders.filter { it.isActive })
}
  1. A.LazyColumn always recomposes along with its parent
  2. B.Composables with list parameters can never skip
  3. C.The theme forces every child to recompose each time
  4. D.filter returns a new list instance, so it looks changed
Show answer

Answer: D. filter returns a new list instance, so it looks changed

Under strong skipping (default with Kotlin 2.0.20+ Compose compiler), unstable parameters such as List are compared by instance. A new list from filter each time is a new instance. Hoist the filtering into the ViewModel or wrap it in remember(orders).

51. Content under the status bar

After raising targetSdk to 35, screens on Android 15 draw under the status and navigation bars. Why?

  1. A.It is a bug in Android 15 that a patch will fix
  2. B.The app theme lost its colorPrimary attribute
  3. C.Apps targeting 35 are edge-to-edge; handle insets
  4. D.targetSdk 35 removes support for all toolbars
Show answer

Answer: C. Apps targeting 35 are edge-to-edge; handle insets

Apps targeting Android 15 (API 35) are displayed edge-to-edge by default. Use Scaffold insets, WindowInsets modifiers like safeDrawingPadding, or ViewCompat inset listeners so content avoids system bars.

52. Decryption fails after new fingerprint

An app encrypts a token with a Keystore key that requires biometric authentication. After the user adds a new fingerprint, decryption fails with KeyPermanentlyInvalidatedException. Why?

  1. A.All Keystore keys expire automatically after 30 days
  2. B.New biometric enrollment invalidates such keys by default
  3. C.The token was stored in the wrong app folder
  4. D.Adding a fingerprint deletes all app data
Show answer

Answer: B. New biometric enrollment invalidates such keys by default

Keys created with user authentication are invalidated on new biometric enrollment by default (setInvalidatedByBiometricEnrollment is true). Catch the exception, delete the key, and ask the user to sign in again to re-create it.

53. Manifest receiver never fires

A receiver declared in the manifest for an implicit broadcast stopped working when the app targeted Android 8.0+. What should it use instead?

  1. A.A higher priority value in the intent filter
  2. B.android:exported="false" set on the receiver
  3. C.A sticky broadcast sent from the app itself
  4. D.A runtime-registered receiver or WorkManager
Show answer

Answer: D. A runtime-registered receiver or WorkManager

Since Android 8.0, apps cannot receive most implicit broadcasts through manifest receivers. Register receivers in code while the app is running, or use WorkManager with constraints (such as network) for background reactions.

54. Observer called twice after back

A fragment observes LiveData with observe(this) in onViewCreated. After navigating away and back, each update fires the callback twice. Why?

  1. A.The fragment outlives its view; use viewLifecycleOwner
  2. B.LiveData always delivers each value twice
  3. C.onViewCreated runs twice per view creation
  4. D.The ViewModel was created twice by mistake
Show answer

Answer: A. The fragment outlives its view; use viewLifecycleOwner

A fragment on the back stack keeps living while its view is destroyed and recreated. Observing with the fragment as owner adds a new observer each time the view is recreated. viewLifecycleOwner removes observers when the view is destroyed.

55. Duplicate screens from notification taps

Tapping a chat notification several times stacks several copies of the same ChatActivity. What prevents duplicates when it is already on top?

  1. A.Start it with FLAG_ACTIVITY_SINGLE_TOP and handle onNewIntent
  2. B.Call finish() in onCreate of every ChatActivity
  3. C.Declare the activity with android:exported set to false
  4. D.Use FLAG_ACTIVITY_NO_ANIMATION on the intent
Show answer

Answer: A. Start it with FLAG_ACTIVITY_SINGLE_TOP and handle onNewIntent

With singleTop behaviour, if the target Activity is already at the top of the task, Android delivers the new intent to onNewIntent instead of creating another instance. The Activity should then update itself from the new intent data. Apps using a single Activity with Navigation often route the intent to a destination instead.

56. Periodic sync every 5 minutes

A developer schedules a WorkManager PeriodicWorkRequest to sync every 5 minutes, but it runs only about every 15 minutes. Why?

  1. A.WorkManager rounds all intervals up to one hour
  2. B.The minimum repeat interval for periodic work is 15 minutes
  3. C.The phone must be charging for periodic work to run
  4. D.Periodic work only runs while the app is visible
Show answer

Answer: B. The minimum repeat interval for periodic work is 15 minutes

WorkManager enforces a 15-minute minimum for periodic work to protect battery, and Doze and app standby buckets can delay it further. Needs for near real-time updates are better met with push messages that trigger a sync, or a foreground service when the user is actively waiting.

57. Gallery app on Android 13

A gallery app targeting Android 13 still requests READ_EXTERNAL_STORAGE and cannot see images. What changed?

  1. A.Android 13 removed app access to images completely
  2. B.It must request READ_MEDIA_IMAGES and READ_MEDIA_VIDEO
  3. C.It must request the CAMERA permission instead
  4. D.Images are only visible after the phone reboots
Show answer

Answer: B. It must request READ_MEDIA_IMAGES and READ_MEDIA_VIDEO

From Android 13 (API 33), READ_EXTERNAL_STORAGE no longer grants media access for apps targeting it. Apps request READ_MEDIA_IMAGES, READ_MEDIA_VIDEO or READ_MEDIA_AUDIO for the types they need. Android 14 added partial access, where users pick selected photos only.

58. Exposing native code to a WebView

An Android app calls `addJavascriptInterface(bridge, "App")` on a WebView that can navigate to any URL. What is the danger?

  1. A.The WebView will refuse to load any HTTPS pages
  2. B.Any page loaded in it can call the exposed native methods
  3. C.JavaScript is disabled as soon as a bridge is added
  4. D.The bridge works only with local HTML files
Show answer

Answer: B. Any page loaded in it can call the exposed native methods

Methods annotated with @JavascriptInterface are callable by any script in the WebView, including scripts on third-party or attacker pages. Restrict navigation to trusted origins, expose minimal methods, validate inputs, and consider WebMessage APIs with origin checks.

59. Attacker installs a CA certificate

An attacker convinces a user to install a custom CA certificate on an Android 13 phone to intercept your app's HTTPS traffic. By default, what happens?

  1. A.All apps trust the new CA and all traffic is readable
  2. B.Apps targeting API 24+ ignore user-added CAs by default
  3. C.The phone wipes itself when a CA is installed
  4. D.Only Wi-Fi traffic is affected, not mobile data
Show answer

Answer: B. Apps targeting API 24+ ignore user-added CAs by default

Since Android 7 (API 24), apps by default trust only system certificate authorities, not ones the user adds. This blocks the simple "install my certificate" attack. Apps that opt in to user CAs in their network security config lose that protection.

60. Raising the minimum OS version

An app raises minSdk from 24 to 26. What happens to users on Android 7.0 devices (API 24)?

  1. A.They keep their installed version but receive no further updates
  2. B.Their installed copy is uninstalled automatically by Play
  3. C.Their app keeps updating, but new features are hidden
  4. D.They are moved to the beta track automatically
Show answer

Answer: A. They keep their installed version but receive no further updates

Play delivers updates only to devices that meet the new release's requirements, so older devices stay on the last compatible version. The app keeps working there until the server stops supporting it. Check analytics for how many active users are affected before raising minSdk or the iOS deployment target.

Ready to test yourself?

Take a 15-question Android MCQ test with a score at the end, or practise the full Android question bank and earn points on the leaderboard.