Skip to content

PhonePe Android App Development Interview Questions & Answers

Curated interview practice questions with answers. These are practice scenarios, not a verified list of past interview questions.

About PhonePe hiring for Android App Development roles

PhonePe is an Indian digital payments and financial services company, majority-owned by Walmart, known for UPI payments, insurance and investment distribution, and the Pincode commerce app. Its engineering teams build high-scale payments systems. It hires for software engineers, SDETs, data analysts, data engineers and data scientists.

For Android App Development roles, this PhonePe practice set focuses most on Room, DataStore and WorkManager (7), Coroutines and Flow (7), App Architecture, MVVM and Hilt (6), Networking and APIs (4). It has 6 easy, 20 medium, 4 hard questions, so you can start easy and work up to the harder ones.

Company type
Fintech
Headquarters
Bengaluru, India
Main India offices
Bengaluru, Pune

PhonePe Android App Development interview process

  1. Online coding assessment
  2. Machine coding round
  3. DSA or problem solving round
  4. Design or hiring manager round
  5. HR round

Typical pattern based on public candidate experiences. Rounds vary by role, team and year.

Solved for PhonePeStart practicing
3 QUESTIONS FREESign up to unlock the full question bank and track your progress.Unlock every question and answer, then continue practising company-wise and package-wise.
3 questions free Unlock the rest in seconds
01

A user complains the app uses too much storage. How do you find and reduce it?

Save to ▾
Show answer
Check Settings storage breakdown and inspect filesDir, cacheDir and databases with Device Explorer. Common culprits are unbounded image caches, old downloaded files, logs, and databases that never delete old rows. Set size limits on Coil and OkHttp caches, use cacheDir for temporary files so the system can clear them, delete old records with a periodic WorkManager job, and compress or downscale stored images.
02

A new developer asks why we need interfaces for every repository. How do you answer?

Save to ▾
Show answer
Interfaces let us swap implementations, mainly fakes in tests and alternate data sources, and they define clear contracts between modules so feature modules depend on abstractions instead of concrete network code. But the rule is not absolute: if a class has one implementation and is easy to test directly, an interface adds noise. I would explain the reason with an example test and our team convention.
03

How does Kotlin handle null safety?

Save to ▾
Show answer
Kotlin's type system separates nullable types (String?) from non-nullable types (String). The compiler stops you calling methods on a nullable value without a check. You use the safe call operator ?., the Elvis operator ?: for defaults, smart casts after null checks, or !! to force a value and risk a NullPointerException. This moves most null crashes from runtime to compile time, which is a major reason Android adopted Kotlin.
04

How do you pass data between screens and what is the size limit?

Save to ▾
Show answer
Traditionally through Intent extras or Fragment arguments as a Bundle with primitives, Parcelable or Serializable objects. Bundles go through Binder transactions, which have a shared limit of about 1 MB, so large data causes TransactionTooLargeException. Pass an ID instead and load the data from a repository or database on the next screen. With Navigation, use type-safe route arguments and a shared ViewModel only when screens truly share state.
05

What is the Paging 3 library and how does it work with Room?

Save to ▾
Show answer
Paging 3 loads large datasets in pages to save memory and network. A PagingSource loads pages, Pager exposes Flow<PagingData>, and the UI uses LazyPagingItems in Compose or PagingDataAdapter in RecyclerView. With a RemoteMediator, the network fills Room and Room acts as the PagingSource, giving offline support and a single source of truth. Use cachedIn(viewModelScope) to survive configuration changes.
06

Write a Room database class with a migration that adds a column.

Save to ▾
Show answer
@Database(entities = [NoteEntity::class], version = 2, exportSchema = true) abstract class AppDatabase : RoomDatabase() { abstract fun noteDao(): NoteDao } val MIGRATION_1_2 = object : Migration(1, 2) { override fun migrate(db: SupportSQLiteDatabase) { db.execSQL("ALTER TABLE notes ADD COLUMN pinned INTEGER NOT NULL DEFAULT 0") } } val db = Room.databaseBuilder(context, AppDatabase::class.java, "app.db") .addMigrations(MIGRATION_1_2) .build()
07

Write a function that retries a suspend call with exponential backoff.

Save to ▾
Show answer
suspend fun <T> retryWithBackoff( times: Int = 3, initialDelay: Long = 500, factor: Double = 2.0, block: suspend () -> T ): T { var delayMs = initialDelay repeat(times - 1) { try { return block() } catch (e: IOException) { /* retry */ } delay(delayMs) delayMs = (delayMs * factor).toLong() } return block() }
08

A screen shows a snackbar again every time the device rotates. How do you fix it?

Save to ▾
Show answer
The event is probably stored as state in a StateFlow or LiveData, so it is re-delivered when the new Activity collects again. Options: model it as part of UI state and have the UI call a function like onMessageShown() to clear it after display, which is the approach the official guidance recommends; or send it through a Channel exposed with receiveAsFlow so each event is consumed once. Collect with lifecycle awareness to avoid losing events.
09

The API team wants to switch to GraphQL. What changes on the Android side?

Save to ▾
Show answer
You would use a client like Apollo Kotlin, which generates type-safe models from the schema and queries at build time, and supports normalised caching and coroutines. Benefits are fetching exactly the fields a screen needs and fewer round trips. Costs include learning the tooling, more complex caching and error handling since errors can come with partial data. I would pilot it on one feature before migrating everything.
10

What is a BroadcastReceiver and what limits apply since Android 8?

Save to ▾
Show answer
A BroadcastReceiver responds to broadcast intents. Since Android 8, most implicit broadcasts cannot be received by receivers declared in the manifest; you register them at runtime with registerReceiver while the app is running. A few exemptions exist, like BOOT_COMPLETED and locale change. Since Android 14, runtime receivers must specify RECEIVER_EXPORTED or RECEIVER_NOT_EXPORTED. onReceive runs on the main thread, so keep it short or use goAsync.
11

What is a use case in the domain layer and when is it worth adding?

Save to ▾
Show answer
A use case is a class with one job, like GetDiscountedCartTotalUseCase, typically exposing operator fun invoke. It holds business logic that combines repositories or is reused by several ViewModels. It keeps ViewModels thin and logic testable. For simple apps where the ViewModel only calls one repository method, use cases add boilerplate without benefit, so the domain layer is optional in the official guidance.
12

Write a Flow that combines user settings and product list into one UI state.

Save to ▾
Show answer
val uiState: StateFlow<HomeState> = combine( settingsRepo.settings, productRepo.products ) { settings, products -> HomeState( currency = settings.currency, items = products.sortedBy { it.price } ) }.stateIn( scope = viewModelScope, started = SharingStarted.WhileSubscribed(5000), initialValue = HomeState() )
13

Tell me about a time you worked closely with a backend team to solve a problem.

Save to ▾
Show answer
Situation: our feed took four seconds to load because the app made six API calls. Task: reduce load time. Action: I measured each call with network profiling, shared a timeline with the backend team, and we agreed on one aggregated endpoint with only the fields the screen used, plus pagination. I updated the client and added caching. Result: load time dropped to under one second and both teams reused the approach.
14

What is unidirectional data flow?

Save to ▾
Show answer
In unidirectional data flow, state flows down from the ViewModel to the UI, and events flow up from the UI to the ViewModel. The UI never changes state directly; it calls functions like onAddToCart(id), the ViewModel updates an immutable state object, and the UI re-renders. This makes state changes predictable, traceable and testable, and it is the model that Jetpack Compose is built around.
15

QA says a bug happens only on one manufacturer's phones. How do you debug it?

Save to ▾
Show answer
Get exact device model, OS and skin versions, and steps. Use a remote device lab or Firebase Test Lab to reproduce if you lack the device. Collect logcat and bug reports, and compare behaviour around known differences like battery optimisation, autostart restrictions, custom permission dialogs or WebView versions. Add targeted logging or a non-fatal Crashlytics event, then apply a fix guarded by checks only if necessary.
16

The app shows an ANR when the user taps Save. The save function is suspend. What could be wrong?

Save to ▾
Show answer
A suspend function does not automatically run off the main thread. If it calls blocking code, like a synchronous file write, a SharedPreferences commit, a blocking SDK or heavy JSON parsing, it still blocks the main thread. Check with a profiler or StrictMode. Fix by wrapping the blocking part in withContext(Dispatchers.IO) or Default, or by using main-safe APIs such as Room suspend DAOs or DataStore.
17

How does coroutine cancellation work, and why must code be cooperative?

Save to ▾
Show answer
Cancelling a Job sets it to cancelling state, and the next suspension point such as delay, withContext or a suspending library call throws CancellationException. A tight CPU loop with no suspension keeps running, so check isActive or call ensureActive() or yield() inside it. Do not swallow CancellationException in a generic catch block; rethrow it. Use finally or withContext(NonCancellable) for cleanup that must finish.
18

What is SavedStateHandle and when do you use it?

Save to ▾
Show answer
SavedStateHandle is a key-value map available to a ViewModel that is saved into the instance state Bundle, so it survives process death as well as configuration changes. Navigation arguments are also placed in it automatically. Use it for small, essential UI state like a search query, selected filter or the ID of the item being edited. Do not put large lists in it; reload those from the repository.
19

A Room query on a large table is slow and causes jank. How do you fix it?

Save to ▾
Show answer
Make sure the query runs off the main thread using suspend or Flow. Use EXPLAIN QUERY PLAN to check for full table scans, and add an @Index on columns used in WHERE, JOIN or ORDER BY. Select only needed columns into a smaller data class instead of SELECT *, page results with Paging 3, and avoid loading large blobs into lists. Wrap bulk inserts in a single transaction.
20

Explain StateFlow versus SharedFlow.

Save to ▾
Show answer
StateFlow is a hot flow that always has a current value, replays only the latest value to new collectors and skips emissions equal to the current value. It is ideal for UI state. SharedFlow is a configurable hot flow with no required initial value, optional replay and buffer, and it emits every value including duplicates. It suits one-off events like navigation or snackbars, though many teams prefer a Channel or state-based events for those.
21

What is Retrofit and how does it work with coroutines?

Save to ▾
Show answer
Retrofit is a type-safe HTTP client that turns an annotated Kotlin interface into API calls. You declare methods with @GET, @POST, @Path, @Query and @Body, and Retrofit generates the implementation using OkHttp underneath. Marking methods as suspend makes them main-safe and cancellable, returning the parsed body directly or Response<T> when you need status codes and headers. A converter like kotlinx.serialization or Moshi parses JSON.
22

Write code to fetch two API results in parallel inside a ViewModel.

Save to ▾
Show answer
fun loadDashboard() { viewModelScope.launch { _state.value = UiState.Loading try { val (profile, orders) = coroutineScope { val p = async { repo.getProfile() } val o = async { repo.getOrders() } p.await() to o.await() } _state.value = UiState.Success(profile, orders) } catch (e: IOException) { _state.value = UiState.Error("Network error") } } }
23

How do you migrate from SharedPreferences to DataStore without losing user data?

Save to ▾
Show answer
Use SharedPreferencesMigration when creating the DataStore: preferencesDataStore(name = "settings", produceMigrations = { ctx -> listOf(SharedPreferencesMigration(ctx, "old_prefs")) }). On first access, DataStore copies the values and deletes the old file. Update all reads and writes to go through the new repository at once, because mixing both stores leads to inconsistent values. Test the migration with existing preference files.
24

How do you handle one-time navigation after a successful login in MVVM?

Save to ▾
Show answer
Recommended practice is to reflect the result in state, such as isLoggedIn = true in UiState, and have the UI react with LaunchedEffect(state.isLoggedIn) to navigate, then the ViewModel marks it consumed if needed. An alternative is a Channel exposed as Flow and collected with lifecycle awareness. Avoid calling navigation directly from the ViewModel, because it should not reference NavController or views.
25

What is CompositionLocal and when should you use it?

Save to ▾
Show answer
CompositionLocal passes data implicitly down the tree without threading it through every parameter, like LocalContext, LocalDensity or MaterialTheme values. Create one with compositionLocalOf when values change often, or staticCompositionLocalOf when they rarely change. Use it for cross-cutting values like theme or analytics loggers, not for regular screen data, because implicit dependencies make composables harder to understand and test.
26

How do you unit test a ViewModel that uses coroutines and StateFlow?

Save to ▾
Show answer
Use kotlinx-coroutines-test. Replace Dispatchers.Main with a test dispatcher using Dispatchers.setMain in a JUnit rule, run tests in runTest so delays are skipped with virtual time, and inject dispatchers into repositories instead of hardcoding Dispatchers.IO. For flows, collect values with Turbine or backgroundScope and assert the sequence, such as Loading then Success. Fake repositories make results deterministic.
27

How do you share data between two screens in a checkout flow?

Save to ▾
Show answer
Options in order of preference: pass small IDs as navigation arguments and load the rest from a repository; store flow data in a repository scoped to the session; or use a ViewModel scoped to a nested navigation graph so all checkout screens share it and it is cleared when the flow ends. Avoid activity-scoped ViewModels holding everything, and avoid passing large objects through arguments.
28

How do you handle a code review comment you disagree with?

Save to ▾
Show answer
I first assume the reviewer has a reason and ask questions to understand it. If I still disagree, I explain my reasoning with evidence, such as documentation, a benchmark or a test, and suggest a short call if comments go back and forth. If it is a matter of preference, I follow the team convention. The goal is the best code for the team, not winning the argument.
29

You need to cache API responses so the home screen opens instantly. What approach do you take?

Save to ▾
Show answer
Store the response in Room, or a file for a single blob, and have the screen observe the cached data immediately while a refresh runs in the background, known as stale-while-revalidate. Store a timestamp to decide when cache is too old. HTTP caching with OkHttp Cache can also help for GET requests with proper cache headers. Show cached content first, then update smoothly when fresh data arrives.
30

What conflict strategies does Room offer for inserts?

Save to ▾
Show answer
@Insert(onConflict = ...) supports REPLACE, which deletes the old row and inserts the new one; IGNORE, which skips the insert; ABORT, the default, which rolls back the transaction; and others. REPLACE can trigger cascading deletes on foreign keys because it deletes first. @Upsert, added in Room 2.5, inserts or updates without deleting, making it safer for syncing server data into local tables.

Tips to crack the PhonePe Android App Development interview

  • Practise machine coding problems and focus on clean, extensible object-oriented design within the time limit.
  • Understand how UPI transactions work at scale, including idempotency, retries and reconciliation.
  • For analyst roles, prepare cases on transaction success rates, merchant adoption and fraud detection metrics.
  • Keep a one-minute story ready for every project on your CV: problem, your part, tools, result.
  • Practise the hard questions on this page aloud; PhonePe interviewers usually ask follow-ups on your first answer.

Frequently asked questions

How many rounds are there in the PhonePe Android App Development interview?

Candidates usually go through 5 stages: Online coding assessment; Machine coding round; DSA or problem solving round; Design or hiring manager round; HR round. The exact rounds change with the role, team, location and your experience, so confirm the process with the recruiter.

Which topics should I prepare for the PhonePe Android App Development interview?

This practice set leans on Room, DataStore and WorkManager (7), Coroutines and Flow (7), App Architecture, MVVM and Hilt (6), Networking and APIs (4). Revise those first, then practise explaining one or two of your own projects end to end, because most PhonePe interviewers ask you to walk through real work.

Are these the exact questions asked at PhonePe?

No. These are curated practice questions that match the skills and difficulty usually tested in fintech interviews like PhonePe's. They are not a leaked or verified list of past questions.

Where does PhonePe hire Android App Development professionals in India?

PhonePe has large teams in Bengaluru, Pune. Openings and locations change often, so check the PhonePe careers page and filter by city and experience.

Android App Development questions for similar companies