20 najczesciej zadawanych pytan rekrutacyjnych z Jetpack Compose w 2026
20 najczesciej zadawanych pytan na rozmowie kwalifikacyjnej z Jetpack Compose: rekompozycja, zarzadzanie stanem, nawigacja, wydajnosc i wzorce architektoniczne.

Jetpack Compose stal sie standardowym zestawem narzedzi UI w rozwoju aplikacji Android. Rozmowy kwalifikacyjne na stanowiska Android regularnie weryfikuja znajomosc Compose — od mechanizmow rekompozycji po zarzadzanie stanem i optymalizacje wydajnosci. Ponizej znajduje sie 20 najczesciej zadawanych pytan wraz ze szczegolowymi odpowiedziami i przykladami kodu.
Kazde pytanie zawiera ustrukturyzowana odpowiedz i przyklad kodu. Pytania sa uporzadkowane wedlug rosnacego poziomu trudnosci: podstawy, poziom sredniozaawansowany, a nastepnie zaawansowany.
Podstawy Jetpack Compose
1. Jaka jest roznica miedzy Compose a systemem widokow XML?
Compose wykorzystuje paradygmat deklaratywny: interfejs uzytkownika jest opisywany jako funkcja stanu, a framework automatycznie zajmuje sie aktualizacjami. Tradycyjny system XML jest imperatywny — widokami trzeba recznie manipulowac za pomoca findViewById lub View Binding.
// Compose: UI updates automatically when count changes
@Composable
fun Counter() {
var count by remember { mutableStateOf(0) } // Reactive state
Button(onClick = { count++ }) { // UI declaration
Text("Clicks: $count") // Recomposed automatically
}
}W Compose nie trzeba wyszukiwac referencji do TextView i recznie jej aktualizowac — rekompozycja zajmuje sie wszystkim.
2. Czym jest rekompozycja?
Rekompozycja to proces, w ktorym Compose ponownie wywoluje funkcje @Composable, gdy zmienia sie ich stan. Tylko funkcje, ktorych parametry ulegly zmianie, sa ponownie wykonywane, co optymalizuje wydajnosc.
@Composable
fun UserCard(name: String, age: Int) {
Column {
Text("Name: $name") // Recomposed only if name changes
Text("Age: $age") // Recomposed only if age changes
StaticBadge() // Not recomposed if its inputs remain the same
}
}
@Composable
fun StaticBadge() {
Text("Static badge") // Compose knows this function is stable
}Kluczowa informacja na rozmowe: rekompozycja jest optymistyczna (Compose zaklada, ze moze zostac anulowana) i nieuporzadkowana (kolejnosc wykonywania composables nie jest gwarantowana).
3. Co robi remember?
remember zachowuje wartosc pomiedzy rekompozycjami. Bez remember kazda rekompozycja resetowalaby zmienna do jej poczatkowej wartosci.
@Composable
fun InputField() {
// ✅ Value survives recompositions
var text by remember { mutableStateOf("") }
// ❌ Without remember, text resets to "" on every recomposition
// var text by mutableStateOf("")
TextField(
value = text,
onValueChange = { text = it }, // Triggers recomposition
label = { Text("Enter text") }
)
}4. Jaka jest roznica miedzy remember a rememberSaveable?
remember zachowuje wartosci pomiedzy rekompozycjami, ale traci je przy zmianach konfiguracji (obrot ekranu). rememberSaveable utrwala wartosci przez zmiany konfiguracji, wykorzystujac mechanizm SavedInstanceState.
@Composable
fun SearchBar() {
// Lost after screen rotation
var query by remember { mutableStateOf("") }
// Preserved after screen rotation
var savedQuery by rememberSaveable { mutableStateOf("") }
TextField(
value = savedQuery,
onValueChange = { savedQuery = it },
placeholder = { Text("Search...") }
)
}Zarzadzanie stanem w Compose
5. Czym jest state hoisting?
State hoisting oznacza przenoszenie stanu z composable do jego rodzica. Komponent potomny staje sie bezstanowy: otrzymuje stan jako parametry i powiadamia o zmianach za pomoca callbackow.
// ✅ Stateless composable — easy to test and reuse
@Composable
fun EmailInput(
email: String, // State provided by parent
onEmailChange: (String) -> Unit, // Callback to parent
modifier: Modifier = Modifier
) {
TextField(
value = email,
onValueChange = onEmailChange,
label = { Text("Email") },
modifier = modifier
)
}
// Parent manages the state
@Composable
fun LoginForm() {
var email by remember { mutableStateOf("") }
EmailInput(
email = email,
onEmailChange = { email = it } // Parent controls state
)
}Ten wzorzec jest fundamentalny w Compose i czesto pojawia sie na rozmowach kwalifikacyjnych.
6. Jak dziala derivedStateOf?
derivedStateOf tworzy stan pochodny, ktory wyzwala rekompozycje tylko wtedy, gdy wynik obliczen sie zmieni, a nie przy kazdej modyfikacji zrodla.
@Composable
fun FilteredList(items: List<String>) {
var searchQuery by remember { mutableStateOf("") }
// Recalculated only when the filtered result actually changes
val filteredItems by remember(items) {
derivedStateOf {
items.filter { it.contains(searchQuery, ignoreCase = true) }
}
}
Column {
TextField(value = searchQuery, onValueChange = { searchQuery = it })
LazyColumn {
items(filteredItems) { item -> Text(item) }
}
}
}Ten mechanizm jest przydatny, gdy stan zmienia sie czesto, ale wynik pochodny zmienia sie rzadko (np. filtrowana lista, przycisk wlaczony/wylaczony na podstawie walidacji formularza).
7. Jaka jest roznica miedzy StateFlow a State<T> w Compose?
StateFlow (Kotlin coroutines) to reaktywny strumien z ViewModelu. State<T> to natywny mechanizm Compose do wyzwalania rekompozycji. W praktyce StateFlow jest zbierany w composable za pomoca collectAsStateWithLifecycle().
class UserViewModel : ViewModel() {
private val _uiState = MutableStateFlow(UserUiState())
val uiState: StateFlow<UserUiState> = _uiState.asStateFlow()
}
@Composable
fun UserScreen(viewModel: UserViewModel = viewModel()) {
// Converts StateFlow to State<T> for Compose
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
Text("Hello, ${uiState.userName}")
}Zalecane jest uzywanie collectAsStateWithLifecycle() (zamiast collectAsState()), poniewaz respektuje cykl zycia i zatrzymuje zbieranie, gdy ekran nie jest juz widoczny.
Efekty uboczne i cykl zycia
8. Jakie sa glowne efekty uboczne w Compose?
Efekty uboczne pozwalaja na uruchamianie kodu nie-composable (wywolania sieciowe, logowanie, nawigacja) w kontrolowany sposob. Trzy glowne to:
@Composable
fun AnalyticsScreen(screenName: String) {
// LaunchedEffect: runs once when screenName changes
LaunchedEffect(screenName) {
analyticsTracker.logScreenView(screenName) // Suspended call
}
// DisposableEffect: with cleanup (like useEffect with cleanup)
DisposableEffect(Unit) {
val listener = onScrollListener()
scrollView.addListener(listener)
onDispose {
scrollView.removeListener(listener) // Cleanup guaranteed
}
}
// SideEffect: runs after every successful recomposition
SideEffect {
logger.log("Screen recomposed") // Non-suspended code
}
}9. Kiedy uzywac LaunchedEffect vs rememberCoroutineScope?
LaunchedEffect jest powiazany z kompozycja: korutyna zostaje anulowana, gdy composable opuszcza kompozycje lub gdy zmieni sie klucz. rememberCoroutineScope dostarcza zakres kontrolowany przez uzytkownika, przydatny do akcji wyzwalanych przez uzytkownika (klikniecia przyciskow).
@Composable
fun DataScreen(userId: String) {
// ✅ LaunchedEffect: automatic loading tied to lifecycle
LaunchedEffect(userId) {
loadUserData(userId) // Re-launched if userId changes
}
// ✅ rememberCoroutineScope: one-off user action
val scope = rememberCoroutineScope()
Button(onClick = {
scope.launch { refreshData() } // Triggered manually
}) {
Text("Refresh")
}
}Gotowy na rozmowy o Android?
Ćwicz z naszymi interaktywnymi symulatorami, flashcards i testami technicznymi.
Layouty i zaawansowane komponenty
10. Jak dziala LazyColumn i czym rozni sie od RecyclerView?
LazyColumn to odpowiednik RecyclerView w Compose. Komponuje tylko elementy widoczne na ekranie i recykluje composables, ktore wyjda poza widoczny obszar.
@Composable
fun UserList(users: List<User>) {
LazyColumn(
contentPadding = PaddingValues(16.dp),
verticalArrangement = Arrangement.spacedBy(8.dp) // Spacing between items
) {
items(
items = users,
key = { it.id } // Stable key to optimize recompositions
) { user ->
UserCard(user)
}
}
}Wazna uwaga: zawsze nalezy podawac stabilny parametr key, aby unikac niepotrzebnych rekompozycji przy sortowaniu lub usuwaniu elementow.
11. Jak stworzyc wlasny layout?
Compose pozwala na tworzenie wlasnych layoutow za pomoca funkcji Layout. Zastepuje to implementacje niestandardowych ViewGroup z systemu widokow.
@Composable
fun OverlappingRow(
overlapOffset: Dp = (-16).dp, // Negative offset for overlap
content: @Composable () -> Unit
) {
Layout(content = content) { measurables, constraints ->
val placeables = measurables.map { it.measure(constraints) }
val width = placeables.sumOf { it.width } + (overlapOffset.roundToPx() * (placeables.size - 1))
val height = placeables.maxOf { it.height }
layout(width, height) {
var xOffset = 0
placeables.forEach { placeable ->
placeable.placeRelative(xOffset, 0)
xOffset += placeable.width + overlapOffset.roundToPx()
}
}
}
}12. Jak zaimplementowac wlasny motyw za pomoca MaterialTheme?
Tematyka w Compose opiera sie na CompositionLocal. MaterialTheme dostarcza wartosci kolorow, typografii i ksztaltow dostepne w calym drzewie composables.
// Custom color definitions
private val DarkColorScheme = darkColorScheme(
primary = Color(0xFF6200EE),
secondary = Color(0xFF03DAC6),
background = Color(0xFF121212)
)
@Composable
fun AppTheme(content: @Composable () -> Unit) {
MaterialTheme(
colorScheme = DarkColorScheme,
typography = AppTypography, // Custom typography
content = content
)
}
// Usage in a composable
@Composable
fun ThemedCard() {
Card(colors = CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.surface // Theme access
)) {
Text(
text = "Content",
style = MaterialTheme.typography.bodyLarge // Theme typography
)
}
}Nawigacja w Compose
13. Jak dziala nawigacja w Compose?
Compose Navigation korzysta z NavHost z trasami deklarowanymi jako stringi (lub typy serializowalne od Navigation 2.8+).
@Composable
fun AppNavigation() {
val navController = rememberNavController()
NavHost(navController = navController, startDestination = "home") {
composable("home") {
HomeScreen(onNavigateToDetail = { id ->
navController.navigate("detail/$id") // Navigation with argument
})
}
composable(
route = "detail/{userId}",
arguments = listOf(navArgument("userId") { type = NavType.StringType })
) { backStackEntry ->
val userId = backStackEntry.arguments?.getString("userId") ?: ""
DetailScreen(userId = userId)
}
}
}14. Jak przekazywac dane miedzy ekranami?
Proste argumenty (String, Int) przekazuje sie bezposrednio przez trase. W przypadku zlozonych obiektow aktualne zalecenie to uzycie wspoldzielonego ViewModel lub przekazanie jedynie identyfikatora i zaladowanie danych na ekranie docelowym.
// Type-safe navigation with Kotlin Serialization (Navigation 2.8+)
@Serializable
data class ProfileRoute(val userId: String, val tab: String = "info")
// Declaration
composable<ProfileRoute> { backStackEntry ->
val route = backStackEntry.toRoute<ProfileRoute>()
ProfileScreen(userId = route.userId, tab = route.tab)
}
// Navigation
navController.navigate(ProfileRoute(userId = "123", tab = "stats"))Nigdy nie nalezy przekazywac zlozonych zserializowanych obiektow w trasie. Nalezy przekazac ID i pozwolic ekranowi docelowemu zaladowac dane przez ViewModel.
Wydajnosc i optymalizacja
15. Jak zapobiegac niepotrzebnym rekompozycjom?
Trzy glowne strategie minimalizowania niepotrzebnych rekompozycji:
// 1. Use stable classes (data class with immutable properties)
@Stable // Tells Compose this class is stable
data class UserState(
val name: String,
val avatar: String
)
// 2. Extract lambdas with remember
@Composable
fun OptimizedList(onItemClick: (String) -> Unit) {
val stableCallback = remember(onItemClick) { onItemClick }
LazyColumn {
items(100) { index ->
ItemRow(onClick = { stableCallback("item_$index") })
}
}
}
// 3. Use key() to help Compose identify elements
@Composable
fun UserTabs(users: List<User>) {
Column {
users.forEach { user ->
key(user.id) { // Stable identity
UserRow(user)
}
}
}
}16. Jak profilowac wydajnosc aplikacji Compose?
Layout Inspector w Android Studio wyswietla liczniki rekompozycji dla kazdego composable. Flaga debugInspectorInfo i CompositionTracer pomagaja w diagnostyce.
// Enable recomposition counters in debug
@Composable
fun DebugRecomposition(tag: String, content: @Composable () -> Unit) {
val recompositionCount = remember { mutableIntStateOf(0) }
SideEffect {
recompositionCount.intValue++ // Incremented on every recomposition
Log.d("Recomposition", "$tag: ${recompositionCount.intValue} times")
}
content()
}
// Usage
DebugRecomposition("UserCard") {
UserCard(user)
}Dodatkowo Compose Compiler Metrics generuje szczegolowy raport funkcji skippable, restartable oraz klas stabilnych i niestabilnych.
17. Czym jest Modifier i dlaczego jest wazny?
Modifier to uporzadkowany lancuch instrukcji modyfikujacych wyglad i zachowanie composable. Kolejnosc modyfikatorow bezposrednio wplywa na renderowanie.
@Composable
fun ModifierOrderDemo() {
// ❌ Padding THEN background = padding not colored
Text(
text = "Hello",
modifier = Modifier
.padding(16.dp)
.background(Color.Red)
)
// ✅ Background THEN padding = padding is colored
Text(
text = "Hello",
modifier = Modifier
.background(Color.Red)
.padding(16.dp)
)
}Dobra praktyka: zawsze nalezy akceptowac parametr modifier: Modifier = Modifier w composables wielokrotnego uzytku, aby umozliwic personalizacje przez rodzica.
Architektura i zaawansowane wzorce
18. Jak ustrukturyzowac ekran Compose z ViewModelem?
Zalecany wzorzec rozdziela stan UI w data class, zdarzenia w sealed interface, a ViewModel obsluguje logike biznesowa.
// UI State
data class ProfileUiState(
val user: User? = null,
val isLoading: Boolean = false,
val error: String? = null
)
// User events
sealed interface ProfileEvent {
data object Refresh : ProfileEvent
data class UpdateName(val name: String) : ProfileEvent
}
// ViewModel
class ProfileViewModel(private val repo: UserRepository) : ViewModel() {
private val _uiState = MutableStateFlow(ProfileUiState(isLoading = true))
val uiState = _uiState.asStateFlow()
fun onEvent(event: ProfileEvent) {
when (event) {
is ProfileEvent.Refresh -> loadProfile()
is ProfileEvent.UpdateName -> updateName(event.name)
}
}
}
// Compose screen
@Composable
fun ProfileScreen(viewModel: ProfileViewModel = viewModel()) {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
ProfileContent(
uiState = uiState,
onEvent = viewModel::onEvent // Event delegation
)
}19. Jak testowac composables?
Compose udostepnia biblioteke testowa z ComposeTestRule do testow UI i asercji semantycznych.
@get:Rule
val composeTestRule = createComposeRule()
@Test
fun counter_incrementsOnClick() {
composeTestRule.setContent {
Counter() // The composable under test
}
// Verify initial state
composeTestRule.onNodeWithText("Clicks: 0").assertIsDisplayed()
// Simulate a click
composeTestRule.onNodeWithText("Clicks: 0").performClick()
// Verify new state
composeTestRule.onNodeWithText("Clicks: 1").assertIsDisplayed()
}W przypadku testowania bezstanowych composables, osobne testowanie ViewModelu za pomoca standardowych testow JUnit/Turbine jest czesto bardziej efektywne.
20. Jak zintegrowac Compose z istniejaca aplikacja oparta na XML?
Interoperacyjnosc jest dwukierunkowa: ComposeView osadza Compose w XML, a AndroidView uzywa klasycznych widokow wewnatrz Compose.
// Compose in XML (in a Fragment or Activity)
class ProfileFragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
return ComposeView(requireContext()).apply {
setViewCompositionStrategy(
ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed
)
setContent {
AppTheme { ProfileScreen() }
}
}
}
}
// XML View in Compose
@Composable
fun LegacyMapView() {
AndroidView(
factory = { context -> MapView(context).apply { onCreate(null) } },
update = { mapView -> mapView.getMapAsync { /* config */ } }
)
}Migracje nalezy przeprowadzac ekran po ekranie, zaczynajac od najprostszych. Kazdy nowy ekran powinien byc w calosci w Compose, podczas gdy istniejace ekrany migruja stopniowo.
Gotowy na rozmowy o Android?
Ćwicz z naszymi interaktywnymi symulatorami, flashcards i testami technicznymi.
Podsumowanie
Te 20 pytan obejmuje podstawy, ktore kazdy programista Android musi opanowac przed rozmowa kwalifikacyjna z Jetpack Compose. Oto lista kontrolna:
- ✅ Zrozumienie rekompozycji i jej optymistycznego zachowania
- ✅ Opanowanie
remember,rememberSaveableiderivedStateOf - ✅ Systematyczne stosowanie state hoistingu
- ✅ Znajomosc efektow ubocznych (
LaunchedEffect,DisposableEffect,SideEffect) - ✅ Optymalizacja wydajnosci (stabilne klasy, klucze, lambdy)
- ✅ Strukturyzowanie ekranow z ViewModel + UiState + Events
- ✅ Testowanie composables za pomoca
ComposeTestRule - ✅ Obsluga interoperacyjnosci Compose/Views
Zacznij ćwiczyć!
Sprawdź swoją wiedzę z naszymi symulatorami rozmów i testami technicznymi.
Tagi
Udostępnij
Powiązane artykuły

MVVM vs MVI na Androidzie: Którą Architekturę Wybrać w 2026?
Szczegółowe porównanie MVVM i MVI na Androidzie: zalety, ograniczenia, przypadki użycia i praktyczny przewodnik po wyborze właściwej architektury w 2026.

Kotlin Coroutines na Androida: Kompletny Przewodnik 2026
Kompleksowy przewodnik po korutynach Kotlin w programowaniu Android: funkcje suspend, zakresy, dispatchery, Flow i zaawansowane wzorce.

Top 25 pytań rekrutacyjnych z Data Science w 2026 roku
Kompleksowy przegląd 25 najczęstszych pytań na rozmowach kwalifikacyjnych dla data scientistów w 2026 roku — od statystyki i ML po SQL, inżynierię cech i architekturę transformerów.