Interview Questions and Answers (2025 Edition)
1. What is Jetpack Compose and why was it introduced?
Answer:
Jetpack Compose is Android’s modern declarative UI toolkit, allowing developers to build interfaces by declaring UI components in Kotlin. It replaces the imperative XML + View system, enabling:
- Conciseness: Less boilerplate compared to XML layouts.
- Reactivity: UI automatically updates when underlying state changes.
- Kotlin-first: Leverages Kotlin language features (lambdas, coroutines).
- Interoperability: Works alongside existing Views for gradual migration.
2. Explain the concept of “composable functions.”
Answer:
A composable function is a Kotlin function annotated with @Composable. It describes a piece of UI and can call other composables. Key points:
- Stateless by default: Only relies on parameters or ambient state.
- Recomposition: When inputs change, Compose re-invokes the function to update UI.
- Lightweight: No overhead of creating new View instances.
@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
Text(
text = "Hello $name!",
modifier = modifier
)
}
3. How does state handling work in Compose?
Answer:
Jetpack Compose manages UI state through observable types. Common patterns:
- remember { mutableStateOf(...) }: Holds state within a composition.
- rememberSaveable { mutableStateOf(...) }: Persists across configuration changes.
- State<T> flows into recomposition: When state changes, dependent composables recompose.
4. What is recomposition, and how can you optimize it?
Answer:
Recomposition is when Compose re-invokes composables whose inputs (state or parameters) changed. Optimization strategies:
- key scopes: Group lists or looping UI to minimize invalidation.
- derivedStateOf: Compute expensive values only when dependencies change.
- MutableState granularity: Prefer small, focused state variables.
- SideEffect APIs: Use LaunchedEffect, DisposableEffect for non-UI work.
5. Describe LaunchedEffect, SideEffect, and DisposableEffect.
Answer:
They are effect-handling APIs:
- LaunchedEffect(key1, ...): Runs a suspend block when keys change; cancels on recomposition. Ideal for coroutines.
- SideEffect { }: Runs after every successful recomposition. Good for non-suspend callbacks (e.g., analytics).
- DisposableEffect(key1, ...): Provides onDispose { }, useful for cleanup (e.g., unregister listeners).
6. How do you build lists in Jetpack Compose?
Answer:
Use LazyColumn or LazyRow:
val myList = listOf(
Items(
id = 1,
title = "First Title"
)
)
data class Items(
val id : Int,
val title: String
)
LazyColumn {
items(items = myList, key = { it.id }) { item ->
Text(item.title)
}
}
- itemsIndexed: Provides index.
- keys: Helps Compose identify items for animations and minimal recomposition.
7. Explain theming and material components in Compose.
Answer:
Compose provides Material Design out of the box:
- MaterialTheme: Wrap your app and access colors, typography, shapes.
- Dynamic theming: Support dark/light mode via isSystemInDarkTheme().
- Custom themes: Override material defaults by supplying your own palettes.
8. How can you interop between Views and Compose?
Answer:
- Compose in Views: Use ComposeView in XML or an Android ViewGroup.
- Views in Compose: Use AndroidView(factory = { context -> MyLegacyView(context) }) to host existing Views.
9. What is rememberCoroutineScope() used for?
Answer:
Obtains a CoroutineScope bound to the composition. Launch background tasks (e.g., HTTP calls) in response to events inside composables.
10. How do you handle navigation in Compose?
Answer:
Use Jetpack Navigation Compose:
@Composable
fun MyNav() {
val navController = rememberNavController()
NavHost(navController, startDestination = "home") {
composable("home") { HomeScreen(navController) }
composable("details/{itemId}") { backStackEntry ->
DetailsScreen(backStackEntry.arguments?.getString("itemId"))
}
}
}
11. Explain snapshot flow and how to collect it.
Answer:
snapshotFlow { stateVariable } converts Compose state reads into a Kotlin Flow. Can be collected in a LaunchedEffect to react to state changes in coroutines.
12. What are “slots” or slot APIs in Compose?
Answer:
Slot-based APIs allow parent composables to accept children content lambdas—e.g., Scaffold, TopAppBar, or custom containers. This enables flexible UI composition.
@Composable
fun MyContainer(content: @Composable () -> Unit) {
Column { content() }
}
13. How do you implement animations in Compose?
Answer:
Compose offers high-level and low-level APIs:
- High-level: animate*AsState (e.g., animateDpAsState) for simple property changes.
- Low-level: updateTransition for multi-property or sequential animations.
- AnimatedVisibility: Show/hide with built-in animations.
14. Describe recomposition scope and stability.
Answer:
A recomposition scope is a region of the UI tree that can recompose independently. A type is stable if Compose can skip recomposition when its instance remains referentially equal or annotated with @Stable.
15. What testing tools are available for Compose?
Answer:
- Unit tests: Call composables and verify UI state via composeTestRule.setContent {}.
- UI tests: Use Espresso-like APIs (onNodeWithText, performClick).
- Snapshot testing: Capture golden images for regressions.
16. How do you load images efficiently in Compose?
Answer:
Typically via Accompanist Coil:
Image(
painter = painterResource(id = story.imageRes),
contentDescription = story.title,
contentScale = ContentScale.Crop,
modifier = Modifier
.fillMaxWidth()
.height(120.dp)
.clip(RoundedCornerShape(8.dp))
)
Or use AsyncImage from Coil Compose.
17. Explain composition locals.
Answer:
Composition locals provide dependency injection–style values down the tree (e.g., LocalContext, LocalDensity). You can define your own:
val LocalUser = compositionLocalOf<User> { error("No user provided") } CompositionLocalProvider(LocalUser provides currentUser) { // children can read LocalUser.current }
18. How do you manage side‑effects when a composable leaves the composition?
Answer:
Use DisposableEffect:
DisposableEffect(Unit) { val listener = registerListener() onDispose { unregisterListener(listener) } }
19. What are best practices for modularizing a Compose app?
Answer:
- Feature-based modules: Keep UI, ViewModels, and data within feature boundaries.
- Use interfaces: Expose screen navigation through contracts.
- Shared UI library: Extract common components (buttons, cards).
- Design tokens: Centralize colors, typography in a theme module.
Physics Base Animation in Jetpack Compose
20. How does Compose integrate with Kotlin Multiplatform?
Answer:
Currently, Compose for Desktop and Web (Compose Multiplatform) allows sharing UI code across platforms. Core business logic can be in common modules, and platform-specific entrypoints host Compose UIs.
These questions cover the breadth of Jetpack Compose—from basic concepts to best practices and advanced integrations—giving you strong talking points for interviews. Good luck!