Android interview questions
Android questions a Mobile developer can be asked, from first job to senior. Open one to see the strongest answer and why it works.
A new Activity is created after a rotation. Where should you put “load this screen’s data”?
The strongest answer: In a lifecycle-aware place that survives recreation — a ViewModel / equivalent — and re-bind the UI when the new view appears
Why it works: Exactly. Recreation is normal. Own the data above the view; re-bind when the view comes back.
- Treat recreation as the happy path.
- Hold screen state above the view.
- Re-bind when the new view is ready.
- Do not hide process death behind a static.
You need to finish a photo upload after the user leaves the screen. What do you use?
The strongest answer: A platform background API that can survive process death — WorkManager, a foreground service with a visible notification where required, or BGTask / URLSession background — not a naked Thread
Why it works: Exactly. Background work is a contract with the OS, and sometimes with a visible notification.
- Use the platform background API.
- Expect process death.
- Foreground services need a real notification.
- Say whether leave-screen cancels or continues.
In Jetpack Compose, how do you make the screen show a new value?
The strongest answer: I change the state the composable reads, and Compose re-runs the functions that read it. There is no view object to hold on to and set text on
Why it works: Exactly — state in, UI out. The only way to change the screen is to change what it reads.
- State in, UI out.
- Change the state, not the view.
- Use state Compose can track.
- Never poke a composable directly.
Why must an Android ViewModel not hold a reference to an Activity, a Fragment or a View?
The strongest answer: It outlives them — it survives recreation on rotation — so the reference both leaks memory and points at a destroyed screen. The ViewModel exposes state and the screen observes it
Why it works: Exactly — different lifetimes is the reason, and “expose state, let the screen observe” is the shape that follows.
- The ViewModel outlives the screen.
- Hold no View, Activity or Fragment.
- Expose state; the screen observes.
- Use an application context if needed.
A new screen in an Android app — a new Activity, or a destination inside the one you have? How do you decide?
The strongest answer: Default to one Activity hosting destinations: shared navigation, one back stack and scoped view models come free. A separate Activity is for a separate entry point, such as a share target or a launcher shortcut
Why it works: Right — the default is the host, and the exception is an entry point the system itself launches.
- One host, many destinations.
- Default to a single Activity.
- Separate Activity for separate entry points.
- Keep one back stack.
Push works on the first install and dies a month later for some users. What do you check?
The strongest answer: Token rotation — FCM / APNs tokens refresh, users reinstall, they deny then allow — I re-register and upsert the token on the backend
Why it works: Exactly. Tokens are ephemeral. Re-register and replace; do not append forever.
- Listen for token refresh.
- Upsert, do not only insert.
- Handle reinstall and permission flips.
- Log send failures by token age.
Why use Room rather than writing SQLite helpers and cursors by hand?
The strongest answer: Room checks the queries at compile time, maps rows to objects, gives you a migration path and observable queries, and keeps the work off the main thread — all of which you would hand-roll and get wrong
Why it works: Exactly — the win is the compile-time check and the migration story, not only the typing you save.
- Room is a layer, not magic.
- Queries verified at compile time.
- Migrations are explicit and testable.
- You still own the SQL.
LiveData or StateFlow for a screen’s state on Android — what actually differs?
The strongest answer: StateFlow is plain Kotlin with a current value, so it is testable off-device and composes with operators. LiveData is lifecycle-aware by itself; with StateFlow you collect it in a lifecycle-aware way yourself
Why it works: Right — the trade is Android-awareness for testability and operators, and the collection site is where you pay it back.
- Both are observable state.
- StateFlow: plain Kotlin, always a value.
- LiveData: lifecycle-aware on its own.
- Collect flows tied to the lifecycle.
A receiver for a system broadcast fires on one phone and never on another, with the same build.
What do you check?
The strongest answer: How it is registered. Modern Android stopped delivering most implicit broadcasts to manifest-declared receivers, so register it from a running component, scoped to a lifecycle, and unregister in the matching callback
Why it works: Right — registration style decides delivery, and a context-registered receiver must be unregistered or it leaks the screen.
- Registration decides delivery.
- Implicit broadcasts skip manifest receivers.
- Register from a running component.
- Unregister with the lifecycle.
When do you split an Android app into Play feature modules?
The strongest answer: When a large, optional surface is worth the install-time / on-demand complexity and we can measure the download — not to hide code from review, and not for a 20 KB settings screen
Why it works: Exactly. On-demand is a product and ops feature. Review still sees what you deliver.
- Split optional, large surfaces.
- Measure the download.
- Do not use modules to dodge review.
- Keep the base able to start.
A sync job is enqueued every time the user opens the sync screen. After a flaky week, support reports duplicate uploads.
What went wrong, and what do you use instead?
The strongest answer: Every visit enqueued another request, and a flaky week let them pile up. Enqueue it as unique work with a name and a policy, and make the worker idempotent so a retry cannot duplicate an upload
Why it works: Exactly — unique work stops the queue growing and idempotency covers the retries the system will do anyway.
- Name the work; keep it unique.
- Enqueue unique work with a policy.
- Make the worker idempotent.
- Assume the system will retry it.
Product wants the app to hold a foreground service all day so a dashboard is always up to date. How do you handle it?
The strongest answer: I ask what the user gets for the battery and the permanent notification, then offer push or periodic work. A foreground service is for a task the user started and can see, not for freshness
Why it works: Right — the test is whether the user is watching the task, and “always fresh” is a push or on-open problem instead.
- Foreground service equals visible task.
- Ask what the user gains.
- Prefer push or periodic work.
- Never fight background limits.