Skip to content

Android App Development Online Assessment Questions & Answers

40 questions for the online assessment stage — 17 easy, 23 medium.

About the online assessment round

Most companies start with an online test: aptitude and reasoning, a few MCQs on core Android App Development concepts, and one or two short hands-on tasks. It filters a large number of applicants, so speed and accuracy both count.

Tips for the online assessment round

  • Practise under a timer: about one minute per MCQ and 15-20 minutes per hands-on task.
  • Read every option before answering; tests often include two nearly correct choices.
  • Use a stable internet connection, keep your camera on and avoid switching tabs in proctored tests.
  • Attempt the questions you are sure about first, then return to the harder ones.
Solved — online assessmentStart 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 Jetpack Compose and how is it different from XML views?

Save to ▾
Show answer
Jetpack Compose is Android's modern declarative UI toolkit. You describe the UI as Kotlin functions of state, and Compose redraws the parts that change when state changes. With XML views you inflate layouts and imperatively update views with findViewById or binding. Compose removes adapters and much boilerplate, makes theming and previews easier, and interoperates with views through ComposeView and AndroidView, allowing gradual migration.
02

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

How do runtime permissions work in modern Android?

Save to ▾
Show answer
Dangerous permissions like camera, location and notifications (POST_NOTIFICATIONS since Android 13) must be declared in the manifest and requested at runtime. Use the Activity Result API with RequestPermission or RequestMultiplePermissions contracts. Show a rationale when shouldShowRequestPermissionRationale returns true, handle denial gracefully, and direct users to settings if they deny permanently. Request permissions in context, at the moment the feature needs them.
04

Where should API keys go in an Android app?

Save to ▾
Show answer
Anything shipped in the APK can be extracted, so treat client-side keys as public. Keep them out of version control using local.properties or CI secrets injected into BuildConfig, restrict them in the provider console by package name and SHA-1, and keep truly secret keys on your backend, which proxies the calls. Use Play Integrity or App Check to reduce abuse from modified apps.
05

Write a manifest entry for a deep link that opens a product screen.

Save to ▾
Show answer
<activity android:name=".MainActivity" android:exported="true"> <intent-filter android:autoVerify="true"> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <data android:scheme="https" android:host="shop.example.com" android:pathPrefix="/product" /> </intent-filter> </activity> <!-- autoVerify plus assetlinks.json on the domain makes it an App Link -->
06

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

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

Write a Kotlin function that groups a list of orders by city and returns total amount per city.

Save to ▾
Show answer
data class Order(val city: String, val amount: Double) fun totalsByCity(orders: List<Order>): Map<String, Double> = orders.groupBy { it.city } .mapValues { (_, list) -> list.sumOf { it.amount } } // Alternative with one pass: // orders.groupingBy { it.city }.fold(0.0) { acc, o -> acc + o.amount }
09

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

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

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

How do LazyColumn keys improve performance and correctness?

Save to ▾
Show answer
By default, items are identified by position, so inserting or deleting an item can make Compose reuse state for the wrong item and recompose more than needed. Supplying key = { it.id } gives each item a stable identity, so remembered state, animations with animateItem and scroll position follow the right item. Also supply contentType for mixed lists so compositions are reused efficiently. Keys must be unique and Bundle-saveable.
13

How do you model API results and errors cleanly for the UI?

Save to ▾
Show answer
Map transport-level outcomes into a sealed result type in the data layer, for example Success(data), HttpError(code, message) and NetworkError. Catch IOException for connectivity, HttpException for non-2xx codes, and serialization exceptions for bad payloads, and never catch CancellationException. The ViewModel converts these into user-friendly UiState messages. Keep DTOs separate from domain models so API changes do not ripple through the UI.
14

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

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

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) }
17

What happens on a configuration change such as screen rotation?

Save to ▾
Show answer
By default the system destroys and recreates the Activity so it can load resources for the new configuration, like landscape layouts or a new language. Fields in the Activity are lost, but a ViewModel survives because it is kept by the ViewModelStore. Views with IDs restore some state automatically, and rememberSaveable in Compose restores small state. Handling changes manually via configChanges is possible but should be a deliberate choice.
18

After an app update, users report that the app crashes on launch. Logs show a Room migration error. What do you do?

Save to ▾
Show answer
Immediately halt the staged rollout in Play Console. Reproduce by installing the previous version, adding data, and upgrading. The schema version was probably increased without a matching migration, or a migration SQL does not match the new entity. Write the correct Migration, test it with MigrationTestHelper from every supported old version, and release a hotfix. Avoid destructive fallback because it deletes user data.
19

Write a HiltViewModel that exposes a single UiState using StateFlow.

Save to ▾
Show answer
data class ProductUiState( val loading: Boolean = true, val items: List<Product> = emptyList(), val error: String? = null ) @HiltViewModel class ProductViewModel @Inject constructor( private val repo: ProductRepository ) : ViewModel() { private val _state = MutableStateFlow(ProductUiState()) val state: StateFlow<ProductUiState> = _state.asStateFlow() init { refresh() } fun refresh() = viewModelScope.launch { _state.update { it.copy(loading = true, error = null) } runCatching { repo.fetchProducts() } .onSuccess { list -> _state.update { it.copy(loading = false, items = list) } } .onFailure { _state.update { it.copy(loading = false, error = "Try again") } } } }
20

How do you collect a ViewModel StateFlow in a composable?

Save to ▾
Show answer
Use val state by viewModel.uiState.collectAsStateWithLifecycle() from lifecycle-runtime-compose. It collects only while the lifecycle is at least STARTED, saving resources in the background, unlike collectAsState which keeps collecting. Pass the resulting state and callbacks to a stateless screen composable, which keeps the screen previewable and testable without a ViewModel. Obtain the ViewModel with hiltViewModel() or viewModel() at the route level.
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

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

What is structured concurrency?

Save to ▾
Show answer
Structured concurrency means every coroutine runs inside a scope, and the scope does not complete until all its children complete. If the scope is cancelled, all children are cancelled; if a child fails, the failure propagates and cancels siblings and the parent by default. This prevents leaked background work. On Android, viewModelScope and lifecycleScope tie coroutines to component lifecycles so work stops when the screen goes away.
24

Explain the Activity lifecycle.

Save to ▾
Show answer
onCreate runs once to set up the UI and state. onStart makes the activity visible, onResume makes it interactive in the foreground. onPause runs when it loses focus, onStop when it is no longer visible, and onDestroy before it is removed. Rotation by default destroys and recreates the activity. Release resources like camera in onPause or onStop, and save transient UI state with ViewModel and SavedStateHandle.
25

Explain the difference between let, run, with, apply and also.

Save to ▾
Show answer
All are scope functions that run a block on an object. let and also receive the object as it; run, with and apply receive it as this. let, run and with return the lambda result, while apply and also return the object itself. Typical use: let for null checks (user?.let { }), apply for configuring an object, also for side effects like logging, and run or with for computing a result from an object.
26

How do you handle push notifications with Firebase Cloud Messaging?

Save to ▾
Show answer
Extend FirebaseMessagingService, override onNewToken to send the token to your server, and onMessageReceived to handle data messages. Notification messages are shown automatically by the system when the app is in background; data messages are always delivered to your code. Request POST_NOTIFICATIONS on Android 13 and above, create channels, and keep onMessageReceived short, using WorkManager for longer work triggered by the push.
27

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

What is DataStore and why does it replace SharedPreferences?

Save to ▾
Show answer
Jetpack DataStore stores key-value pairs (Preferences DataStore) or typed objects (Proto DataStore) using coroutines and Flow. Unlike SharedPreferences, it is safe to call from the main thread because I/O is asynchronous, it reports errors through exceptions, and updates are transactional. SharedPreferences apply() can still block the main thread during lifecycle events and cause ANRs. Use one DataStore instance per file, ideally as a singleton.
29

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

What is an Android App Bundle and why does Play require it?

Save to ▾
Show answer
An App Bundle (.aab) is a publishing format containing all compiled code and resources. The Play Store generates optimised APKs for each device configuration, delivering only the needed screen density, CPU architecture and language resources, which reduces download size. Since August 2021, new apps on Play must use AAB with Play App Signing, where the store holds the app signing key and you sign uploads with an upload key.
31

What is the role of AndroidManifest.xml?

Save to ▾
Show answer
The manifest declares the app's package identity, components (activities, services, receivers, providers), intent filters, permissions, hardware features, and app-level settings like theme, backup rules and exported flags. Since Android 12, components with intent filters must set android:exported explicitly. Gradle merges manifests from libraries into the final one, so check the merged manifest when unexpected permissions appear in your release build.
32

What are OkHttp interceptors and give common uses.

Save to ▾
Show answer
Interceptors observe or modify requests and responses in the OkHttp chain. Application interceptors run once per call and are used for adding auth headers, common query parameters or logging with HttpLoggingInterceptor. Network interceptors run for each network request including redirects and see the actual network bytes, useful for cache headers. Never log full bodies or tokens in release builds, because logs can leak sensitive data.
33

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

Why does the order of modifiers matter?

Save to ▾
Show answer
Modifiers are applied in a chain from left to right, each wrapping the next. Modifier.padding(16.dp).background(Color.Red) adds padding outside the red area, while background first then padding colours the padding too. Similarly, clickable before padding makes the whole padded area clickable. Thinking of each modifier as wrapping the content after it helps predict layout, size and touch target behaviour.
35

Write a Hilt module that provides Retrofit and a repository binding.

Save to ▾
Show answer
@Module @InstallIn(SingletonComponent::class) object NetworkModule { @Provides @Singleton fun provideRetrofit(): Retrofit = Retrofit.Builder() .baseUrl("https://api.example.com/") .addConverterFactory(Json.asConverterFactory("application/json".toMediaType())) .build() @Provides @Singleton fun provideApi(retrofit: Retrofit): ShopApi = retrofit.create(ShopApi::class.java) } @Module @InstallIn(SingletonComponent::class) abstract class RepoModule { @Binds abstract fun bindRepo(impl: ProductRepositoryImpl): ProductRepository }
36

Write code to save and read a dark mode preference with Preferences DataStore.

Save to ▾
Show answer
val Context.dataStore by preferencesDataStore(name = "settings") private val DARK_MODE = booleanPreferencesKey("dark_mode") class SettingsRepository(private val context: Context) { val darkMode: Flow<Boolean> = context.dataStore.data .catch { emit(emptyPreferences()) } .map { it[DARK_MODE] ?: false } suspend fun setDarkMode(enabled: Boolean) { context.dataStore.edit { it[DARK_MODE] = enabled } } }
37

What is the difference between == and === in Kotlin?

Save to ▾
Show answer
== checks structural equality and calls equals() under the hood, handling nulls safely. === checks referential equality, meaning both references point to the same object in memory. For two data class instances with the same values, == returns true but === returns false unless they are the same instance. For primitive-like types such as Int, === may behave unexpectedly due to boxing, so == is the normal choice.
38

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") } } }
39

Write a stateless text field with a hoisted state and validation message.

Save to ▾
Show answer
@Composable fun EmailField( value: String, onValueChange: (String) -> Unit, error: String?, modifier: Modifier = Modifier ) { OutlinedTextField( value = value, onValueChange = onValueChange, label = { Text("Email") }, isError = error != null, supportingText = { error?.let { Text(it) } }, singleLine = true, modifier = modifier.fillMaxWidth() ) }
40

How do you build a single-activity app with the Navigation component?

Save to ▾
Show answer
Keep one Activity hosting a NavHost, and represent each screen as a Compose destination or Fragment. Define routes in a navigation graph, preferably with type-safe routes using Kotlin serialization in Navigation 2.8 and later. Use nested graphs for flows like login or checkout, pop the back stack with popUpTo when finishing a flow, and handle deep links on destinations. This simplifies shared transitions and state handling.

Frequently asked questions

What happens in the Android App Development online assessment round?

Most companies start with an online test: aptitude and reasoning, a few MCQs on core Android App Development concepts, and one or two short hands-on tasks. It filters a large number of applicants, so speed and accuracy both count.

How do I clear the online assessment round?

Practise under a timer: about one minute per MCQ and 15-20 minutes per hands-on task. Read every option before answering; tests often include two nearly correct choices. Use a stable internet connection, keep your camera on and avoid switching tabs in proctored tests.

Which round comes next?

A typical order is online assessment, telephonic screening, one or two technical rounds, a managerial round and HR. Use the other round pages to practise each stage.