Skip to content

Android App Development Interview Questions in Mumbai

36 practice questions with answers for Android App Development interviews in Mumbai.

Android App Development jobs in Mumbai

Mumbai is India's financial capital, so banks, fintechs, insurers, media and consulting firms drive much of the hiring. Data and QA interviews often include domain questions on payments, risk, trading or customer analytics.

Below are 36 questions that match the mix of service, product and domain employers hiring junior android developers in Mumbai.

Solved for MumbaiStart 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

What is the difference between MVVM and MVI?

Save to ▾
Show answer
Both separate UI from logic. MVI (Model-View-Intent) is stricter: the UI sends intents or actions, a reducer produces a single new immutable state, and side effects are handled separately. It gives one state object per screen and an easy-to-replay history. MVVM is more flexible, sometimes exposing several streams. In practice many Compose apps use MVVM with a single UiState data class, which is close to MVI.
02

What is a sealed class and when would you use it on Android?

Save to ▾
Show answer
A sealed class restricts its subclasses to a known set defined in the same package and module. The compiler knows every subtype, so a when expression over it can be exhaustive without an else branch. On Android it is commonly used to model UI state such as Loading, Success(data) and Error(message), or one-time events. Adding a new subtype causes compile errors wherever handling is missing, which prevents forgotten cases.
03

What is a repository and why is it the single source of truth?

Save to ▾
Show answer
A repository hides where data comes from and gives the rest of the app one API, such as getProducts(): Flow<List<Product>>. With an offline-first design, the repository reads from Room and refreshes from the network in the background, writing results to the database. The UI always observes the database, so it is the single source of truth. This avoids inconsistent copies of data across screens.
04

Explain the network security configuration file.

Save to ▾
Show answer
network_security_config.xml, referenced from the manifest, controls TLS behaviour without code. Since Android 9, cleartext HTTP is blocked by default; you can allow it for specific debug domains. You can trust user-added CAs only in debug builds for tools like Charles or proxies, and define certificate pins with expiry. Keeping cleartext disabled in production is an important security baseline.
05

What file storage options does Android provide and how does scoped storage affect them?

Save to ▾
Show answer
App-specific storage (filesDir, cacheDir, getExternalFilesDir) needs no permission and is deleted on uninstall. Shared media is accessed through MediaStore, and other documents through the Storage Access Framework. Since Android 10, scoped storage blocks broad file system access, and since Android 13 media permissions are split into images, video and audio. The photo picker lets users choose media without any permission, which is preferred.
06

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.
07

How should a Fragment or Activity collect a Flow safely?

Save to ▾
Show answer
Use lifecycleScope.launch with repeatOnLifecycle(Lifecycle.State.STARTED) so collection starts when the UI is visible and stops in the background, avoiding wasted work and crashes from updating views that are not ready. In a Fragment, use viewLifecycleOwner.lifecycleScope because the fragment view can be destroyed while the fragment lives. In Compose, use collectAsStateWithLifecycle(), which does the same lifecycle-aware collection.
08

Write a sealed interface for a screen state and handle it with when.

Save to ▾
Show answer
sealed interface UiState { data object Loading : UiState data class Success(val items: List<String>) : UiState data class Error(val message: String) : UiState } fun render(state: UiState) = when (state) { UiState.Loading -> showProgress() is UiState.Success -> showList(state.items) is UiState.Error -> showError(state.message) }
09

Write a function that safely parses a string to an Int and returns a default on failure.

Save to ▾
Show answer
fun String?.toIntOrDefault(default: Int = 0): Int = this?.trim()?.toIntOrNull() ?: default // Usage val qty = input.text.toString().toIntOrDefault(1) val age = null.toIntOrDefault() // 0
10

What is the difference between launch and async?

Save to ▾
Show answer
launch starts a coroutine for work that returns no result and gives back a Job you can cancel or join. async returns a Deferred<T>, and you call await() to get the result. Use async to run independent tasks in parallel, such as fetching profile and orders together, then awaiting both. Exceptions differ: launch propagates them to the parent immediately, while async holds them until await, though in structured concurrency the parent still gets cancelled.
11

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() )
12

The crash-free rate dropped after a release only on Android 15 devices. How do you respond?

Save to ▾
Show answer
Halt the staged rollout immediately. Filter Crashlytics or Play vitals by OS version to find the top crash, then check Android 15 behaviour changes such as edge-to-edge enforcement, foreground service rules or new restrictions affecting your target SDK. Reproduce on an Android 15 emulator, fix, add a test, and resume rollout with the hotfix. Afterward, add Android 15 devices to the regular test matrix.
13

What is the difference between a started service, bound service and foreground service?

Save to ▾
Show answer
A started service runs after startService until it stops itself. A bound service offers a client-server interface to components that bind to it and stops when all unbind. A foreground service shows a persistent notification and has higher priority, used for music, navigation or ongoing calls. Since Android 14 a foreground service must declare a type such as mediaPlayback or location. For deferrable background work, use WorkManager instead.
14

What is a ContentProvider and when would you build one?

Save to ▾
Show answer
A ContentProvider exposes data through a URI-based interface with query, insert, update and delete, and handles permissions for cross-app access. You need one when sharing data with other apps, providing search suggestions, or supporting widgets that read your data. FileProvider is a ready-made provider for sharing files securely with content URIs instead of file paths. For data used only inside your app, Room is simpler.
15

What are sequences in Kotlin and when are they better than collections?

Save to ▾
Show answer
Sequences evaluate lazily, element by element, while collection operations like map and filter create a new intermediate list at every step. For long chains on large data, or when you only need the first few results with take or first, asSequence() avoids extra allocations. For small lists, plain collections are often faster because sequences add per-element overhead. Measure before switching in performance-critical code.
16

How would you lead an architecture decision when senior developers disagree?

Save to ▾
Show answer
I would write a short decision document stating the problem, two or three options with trade-offs, and constraints such as team skill and timeline, then build a small spike of the top options on a real screen. We review with measurable criteria like test effort and build time, decide, and record it as an architecture decision record. Once decided, everyone commits, and we revisit only if new facts appear.
17

How do you implement theming with Material 3 in Compose?

Save to ▾
Show answer
Wrap the app in a MaterialTheme with a ColorScheme, Typography and Shapes. Use lightColorScheme and darkColorScheme, pick based on isSystemInDarkTheme(), and use dynamicLightColorScheme or dynamicDarkColorScheme on Android 12 and above for Material You colours. Components read MaterialTheme.colorScheme.primary and similar roles, so avoid hardcoding colours. Extend the theme with a custom CompositionLocal for brand-specific tokens.
18

Why should we hire you as a fresher Android developer?

Save to ▾
Show answer
Give evidence, not adjectives. For example: I have built and published two apps using Kotlin and Jetpack Compose, one with Room and Retrofit and an MVVM structure, and both are on my GitHub with a README and tests. I debug by reading logs and stack traces before asking for help, and I am comfortable learning from code reviews. I can contribute to bug fixes and small features from the first month.
19

A crash report shows a NullPointerException in Kotlin code. How can that happen if Kotlin is null safe?

Save to ▾
Show answer
Common causes are the !! operator, platform types from Java code without nullability annotations, accessing a lateinit property before assignment, calling a function from a constructor before properties are initialised, and reflection-based libraries like Gson setting non-null fields to null. Fix it by removing !!, adding annotations or explicit nullable types at the Java boundary, and using a Kotlin-aware serializer such as kotlinx.serialization or Moshi with Kotlin codegen.
20

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.
21

What does R8 do in a release build?

Save to ▾
Show answer
R8 shrinks code by removing unused classes and members, optimises bytecode by inlining and removing dead branches, obfuscates names to make reverse engineering harder and reduce size, and shrinks resources when enabled. It needs keep rules for code accessed by reflection, such as some serialization or SDK classes. Upload the mapping file to Play Console and Crashlytics so obfuscated stack traces can be decoded.
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

What is recomposition?

Save to ▾
Show answer
Recomposition is Compose re-running composable functions whose inputs or read state changed, to update the UI. Compose tries to skip composables whose parameters are stable and unchanged. Composables can run often and in any order, so they must be side-effect free and fast; do not make network calls or write to variables inside them. Use Layout Inspector recomposition counts to find composables recomposing more than expected.
24

What is the difference between List and MutableList, and is List truly immutable?

Save to ▾
Show answer
List is a read-only interface exposing only reading operations, while MutableList adds add, remove and set. A List is not guaranteed immutable: the underlying object might be an ArrayList that another reference can still modify. For real immutability use kotlinx.collections.immutable or create defensive copies. In ViewModels, expose List or StateFlow<List> publicly and keep the mutable version private to protect state.
25

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.
26

The backend team changed a field from a number to a string and the app crashed for all users. How do you prevent this?

Save to ▾
Show answer
Short term, make parsing lenient for that field, for example with a custom serializer, and handle parse failures without crashing. Long term, agree on API contracts with versioning, use contract tests or a shared OpenAPI schema checked in CI, add ignoreUnknownKeys and default values in kotlinx.serialization, and use remote config or a minimum supported app version mechanism. Monitor parse errors as non-fatal events.
27

What is the difference between a deep link and an Android App Link?

Save to ▾
Show answer
A deep link is any intent filter that opens your app for a URI; if several apps can handle it, the user sees a chooser. An App Link is an HTTPS deep link verified with android:autoVerify and a Digital Asset Links file at /.well-known/assetlinks.json on your domain, containing your app's signing certificate fingerprint. Verified links open your app directly. Remember to include the Play App Signing key fingerprint.
28

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.
29

Write a Compose UI test that checks an error appears when login is clicked with an empty email.

Save to ▾
Show answer
@get:Rule val rule = createComposeRule() @Test fun showsErrorForEmptyEmail() { rule.setContent { var state by remember { mutableStateOf(LoginState()) } LoginScreen( state = state, onLogin = { state = state.copy(error = "Email required") } ) } rule.onNodeWithText("Login").performClick() rule.onNodeWithText("Email required").assertIsDisplayed() }
30

What is the difference between @Provides and @Binds in Hilt?

Save to ▾
Show answer
@Provides is used in a module function that constructs and returns an object, needed for third-party classes like Retrofit or Room that you cannot annotate with @Inject. @Binds is used on an abstract function in an abstract module to tell Hilt which implementation to use for an interface, when the implementation already has an @Inject constructor. @Binds generates less code and is slightly more efficient.
31

What does derivedStateOf do and when should you use it?

Save to ▾
Show answer
derivedStateOf creates state computed from other state, and only triggers recomposition when the computed result changes. A classic example is showing a scroll-to-top button: val showButton by remember { derivedStateOf { listState.firstVisibleItemIndex > 0 } }. Without it, every scroll pixel would recompose. Do not use it when the result changes as often as the input, because it only adds overhead.
32

Write a Room entity and DAO for notes with a Flow query.

Save to ▾
Show answer
@Entity(tableName = "notes") data class NoteEntity( @PrimaryKey(autoGenerate = true) val id: Long = 0, val title: String, val body: String, val updatedAt: Long ) @Dao interface NoteDao { @Query("SELECT * FROM notes ORDER BY updatedAt DESC") fun observeAll(): Flow<List<NoteEntity>> @Upsert suspend fun upsert(note: NoteEntity) @Query("DELETE FROM notes WHERE id = :id") suspend fun delete(id: Long) }
33

A crash says IllegalStateException: Can not perform this action after onSaveInstanceState. What is happening?

Save to ▾
Show answer
A fragment transaction or dialog show was committed after the Activity saved its state, usually because a network callback finished while the app was in background. Committing then would lose state. Fix by triggering navigation only when the lifecycle is at least STARTED, collecting events with repeatOnLifecycle, or deferring the action. commitAllowingStateLoss hides the crash but can lose the transaction, so use it only when that is acceptable.
34

How do you test a Compose UI?

Save to ▾
Show answer
Use the Compose testing APIs with createComposeRule or createAndroidComposeRule. Find nodes by text, content description or testTag, then perform actions and assertions, like onNodeWithText("Login").performClick() and onNodeWithTag("error").assertIsDisplayed(). Test stateless composables with fake state for speed. Tests synchronise with Compose automatically. Screenshot testing tools like Roborazzi or Paparazzi catch visual regressions without a device.
35

Explain how you would use Room with Kotlin Multiplatform.

Save to ▾
Show answer
Since Room 2.7, Room supports Kotlin Multiplatform for Android, iOS and desktop. You define entities, DAOs and the database in commonMain, use the bundled SQLite driver, and create the database builder in platform-specific source sets with expect and actual. DAO functions must be suspend or return Flow in common code. This lets teams share the data layer while keeping native UIs, which some Indian product companies are adopting.
36

You need to sync 500 images to a server without overloading the network. How would you design it with coroutines?

Save to ▾
Show answer
Limit concurrency. Use a Semaphore with withPermit around each upload, or flatMapMerge(concurrency = 4) over a flow of files, or Dispatchers.IO.limitedParallelism(4). Use supervisorScope so one failed upload does not cancel the rest, collect failures for retry with backoff, and report progress through a StateFlow. If the sync must survive app exit, run it inside a CoroutineWorker with WorkManager constraints such as network connected.

Companies with offices in Mumbai (5)

Frequently asked questions

Which companies hire Android App Development professionals in Mumbai?

Companies with large offices in Mumbai include Paytm, Zepto, Google, Dream11, TCS. Use their company pages below to practise employer-specific sets.

Are Android App Development interviews in Mumbai different from other cities?

The questions depend more on the company than the city. Mumbai's mix of employers decides the style: Data and QA interviews often include domain questions on payments, risk, trading or customer analytics

How do I find Android App Development jobs in Mumbai?

Shortlist companies with Mumbai offices, keep your CV focused on Android App Development projects, apply on company career pages and job portals with the Mumbai filter, and practise the questions on this page before each interview.