20 Pertanyaan Wawancara Jetpack Compose Teratas di Tahun 2026
20 pertanyaan wawancara Jetpack Compose yang paling sering ditanyakan: recomposition, state management, navigation, performa, dan pola arsitektur.

Jetpack Compose telah menjadi toolkit UI standar untuk pengembangan Android. Wawancara teknis kini secara rutin menguji kemahiran Compose, dari mekanisme recomposition hingga state management dan optimasi performa. Berikut adalah 20 pertanyaan yang paling sering diajukan, dengan jawaban terperinci dan contoh kode.
Setiap pertanyaan mencakup jawaban terstruktur dan contoh kode. Pertanyaan disusun berdasarkan tingkat kesulitan: dasar, menengah, kemudian lanjutan.
Dasar-Dasar Jetpack Compose
1. Apa perbedaan antara Compose dan sistem view XML?
Compose menggunakan paradigma deklaratif: UI dideskripsikan sebagai fungsi dari state, dan framework menangani pembaruan secara otomatis. Sistem XML tradisional bersifat imperatif, memerlukan manipulasi view secara manual melalui findViewById atau 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
}
}Dengan Compose, tidak perlu mencari referensi TextView dan memperbaruinya secara manual. Recomposition menangani semuanya.
2. Apa itu recomposition?
Recomposition adalah proses di mana Compose memanggil ulang fungsi @Composable ketika state-nya berubah. Hanya fungsi yang parameternya telah berubah yang dieksekusi ulang, yang mengoptimalkan performa.
@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
}Poin penting untuk wawancara: recomposition bersifat optimistis (Compose mengasumsikan dapat dibatalkan) dan tidak berurutan (urutan eksekusi composable tidak dijamin).
3. Apa fungsi remember?
remember mempertahankan nilai selama recomposition. Tanpa remember, setiap recomposition akan mengatur ulang variabel ke nilai awalnya.
@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. Apa perbedaan antara remember dan rememberSaveable?
remember mempertahankan nilai selama recomposition tetapi kehilangannya saat configuration change (rotasi layar). rememberSaveable mempertahankan nilai melalui configuration change menggunakan mekanisme 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...") }
)
}State Management di Compose
5. Apa itu state hoisting?
State hoisting berarti memindahkan state dari composable ke induknya. Composable anak menjadi stateless: menerima state sebagai parameter dan memberitahukan perubahan melalui callback. Pola ini penting untuk membangun komponen yang dapat digunakan kembali dan dapat diuji.
// ✅ 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
)
}Pola ini fundamental dalam Compose dan sering muncul dalam wawancara.
6. Bagaimana cara kerja derivedStateOf?
derivedStateOf membuat state turunan yang hanya memicu recomposition ketika hasil perhitungan berubah, bukan pada setiap modifikasi sumber.
@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) }
}
}
}Mekanisme ini berguna ketika state sering berubah tetapi hasil turunannya jarang berubah (misalnya, daftar yang difilter, tombol enabled/disabled berdasarkan validitas form).
7. Apa perbedaan antara StateFlow dan Compose State<T>?
StateFlow (Kotlin coroutines) adalah reactive stream dari ViewModel. State<T> adalah mekanisme native Compose untuk memicu recomposition. Dalam praktiknya, StateFlow dikumpulkan dalam composable melalui collectAsStateWithLifecycle(). Untuk pemahaman lebih dalam tentang coroutine flow, lihat panduan lengkap Kotlin coroutines.
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}")
}Rekomendasi adalah menggunakan collectAsStateWithLifecycle() (bukan collectAsState()) karena menghormati lifecycle dan menghentikan collection ketika layar tidak lagi terlihat.
Side Effect dan Lifecycle
8. Apa saja side effect utama di Compose?
Side effect memungkinkan menjalankan kode non-composable (panggilan jaringan, logging, navigasi) secara terkontrol. Side effect utama meliputi LaunchedEffect, DisposableEffect, SideEffect, dan Keyed SideEffect baru yang diperkenalkan di Compose 1.12.
@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
}
}Pembaruan Compose 1.12: Keyed SideEffect menyediakan overload baru yang mendukung argumen key untuk side effect sekali jalan setiap kali key tertentu berubah. Menurut benchmark resmi, ini bisa hingga 90% lebih cepat dari LaunchedEffect untuk operasi non-suspended.
9. Kapan menggunakan LaunchedEffect vs rememberCoroutineScope?
LaunchedEffect terikat pada composition: coroutine dibatalkan ketika composable meninggalkan composition atau ketika key berubah. rememberCoroutineScope menyediakan scope yang dikontrol pengguna, berguna untuk aksi yang dipicu pengguna (klik tombol).
@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")
}
}Siap menguasai wawancara Android Anda?
Berlatih dengan simulator interaktif, flashcards, dan tes teknis kami.
Layout dan Komponen Lanjutan
10. Bagaimana cara kerja LazyColumn dan bagaimana perbedaannya dengan RecyclerView?
LazyColumn adalah ekuivalen Compose dari RecyclerView. Ini hanya meng-compose elemen yang terlihat di layar dan mendaur ulang composable yang keluar dari jendela yang terlihat.
@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)
}
}
}Poin penting: selalu berikan parameter key yang stabil untuk menghindari recomposition yang tidak perlu saat mengurutkan atau menghapus elemen.
11. Bagaimana cara membuat custom layout?
Compose memungkinkan pembuatan custom layout melalui fungsi Layout. Ini menggantikan implementasi ViewGroup kustom dari sistem view.
@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. Bagaimana cara mengimplementasikan theming kustom dengan MaterialTheme?
Theming di Compose bergantung pada CompositionLocal. MaterialTheme menyediakan nilai warna, tipografi, dan bentuk yang dapat diakses di seluruh composable tree.
// 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
)
}
}Navigasi di Compose
13. Bagaimana cara kerja Compose Navigation?
Compose Navigation menggunakan NavHost dengan route yang dideklarasikan sebagai tipe serializable (Navigation 2.8+). Sejak Navigation 2.9, library mendukung value class dan List<Enum> sebagai tipe argumen tanpa memerlukan implementasi NavType kustom.
@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. Bagaimana cara mengirim data antar layar?
Argumen sederhana (String, Int) diteruskan langsung melalui route. Untuk objek kompleks, rekomendasi saat ini adalah menggunakan ViewModel bersama atau hanya mengirim identifier dan memuat data di layar tujuan. Navigation 2.9 memperkenalkan CollectionNavType<T> untuk argumen berbasis koleksi seperti list dan array.
// 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"))Jangan pernah mengirim objek kompleks yang di-serialize dalam route. Kirim ID dan biarkan layar tujuan memuat data melalui ViewModel. Untuk panduan arsitektur, lihat MVVM vs MVI: Arsitektur Mana yang Dipilih.
Performa dan Optimasi
15. Bagaimana cara mencegah recomposition yang tidak perlu?
Tiga strategi utama untuk meminimalkan recomposition yang tidak perlu:
// 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. Bagaimana cara melakukan profiling performa aplikasi Compose?
Layout Inspector Android Studio menampilkan jumlah recomposition per composable. Compose 1.13 alpha memperkenalkan RecompositionTracer untuk merekam alur invalidasi recomposition. Flag debugInspectorInfo juga membantu diagnostik.
// 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)
}Selain itu, Compose Compiler Metrics menghasilkan laporan terperinci tentang fungsi skippable, restartable, dan kelas stable/unstable.
17. Apa itu Modifier dan mengapa penting?
Modifier adalah rantai instruksi yang terurut yang memodifikasi tampilan dan perilaku composable. Urutan modifier secara langsung memengaruhi rendering.
@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)
)
}Praktik terbaik: selalu terima parameter modifier: Modifier = Modifier dalam composable yang dapat digunakan kembali untuk memungkinkan kustomisasi oleh induk.
Arsitektur dan Pola Lanjutan
18. Bagaimana cara menyusun layar Compose dengan ViewModel?
Pola yang direkomendasikan memisahkan state UI dalam data class, event dalam sealed interface, dan ViewModel menangani logika bisnis.
// 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. Bagaimana cara menguji composable?
Compose menyediakan library pengujian dengan ComposeTestRule untuk UI test dan asersi semantik. Sejak Compose 1.11, API testing v2 menjadi default, dengan v1 deprecated. Perubahan utama adalah peralihan dari UnconfinedTestDispatcher (eksekusi langsung) ke StandardTestDispatcher (eksekusi antrian), yang berarti test sekarang lebih mencerminkan perilaku production.
@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()
// With v2 APIs, advance the clock explicitly
composeTestRule.waitForIdle()
// Verify new state
composeTestRule.onNodeWithText("Clicks: 1").assertIsDisplayed()
}Untuk unit testing composable stateless, menguji ViewModel secara terpisah dengan test JUnit/Turbine standar seringkali lebih efisien.
20. Bagaimana cara mengintegrasikan Compose ke aplikasi berbasis XML yang sudah ada?
Interoperabilitas bersifat dua arah: ComposeView menyematkan Compose dalam XML, dan AndroidView menggunakan view klasik dalam 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 */ } }
)
}Migrasikan layar demi layar, dimulai dengan layar yang paling sederhana. Setiap layar baru harus sepenuhnya dalam Compose, sementara layar yang ada bermigrasi secara bertahap.
Siap menguasai wawancara Android Anda?
Berlatih dengan simulator interaktif, flashcards, dan tes teknis kami.
Sumber
- What's new in the Jetpack Compose August '26 release - Rilis Compose 1.12 dengan Keyed SideEffect, BOM 2026.08.00
- What's new in the Jetpack Compose April '26 release - API testing v2, debugging shared element
- Navigation releases - Navigation 2.9 dengan dukungan value class dan CollectionNavType
- Side effects in Compose - Dokumentasi side effect resmi
Poin Penting untuk Wawancara Jetpack Compose
20 pertanyaan ini mencakup dasar-dasar yang harus dikuasai setiap developer Android untuk wawancara Jetpack Compose. Untuk latihan praktis, coba modul pertanyaan wawancara Jetpack Compose.
- ✅ Pahami recomposition dan perilaku optimistisnya
- ✅ Kuasai
remember,rememberSaveable, danderivedStateOf - ✅ Terapkan state hoisting secara sistematis
- ✅ Ketahui side effect (
LaunchedEffect,DisposableEffect,SideEffect, dan Keyed SideEffect baru) - ✅ Optimalkan performa (stable class, key, lambda)
- ✅ Susun layar dengan ViewModel + UiState + Event
- ✅ Uji composable dengan
ComposeTestRulemenggunakan API testing v2 - ✅ Tangani interoperabilitas Compose/View
Mulai berlatih!
Uji pengetahuan Anda dengan simulator wawancara dan tes teknis kami.
Bisakah kamu menemukan bug di Android?
Satu potongan kode nyata, satu bug tersembunyi, satu percobaan per hari. Tanpa akun untuk mencoba.

Ditulis oleh
Anthony Fillion-MailletPendiri SharpSkill
Developer fullstack selama lebih dari 10 tahun. Ia menjalankan SharpSkill dan bertanggung jawab atas semua yang diterbitkan di sini.
Diperbarui 20 Agustus 2026
Tag
Bagikan
Artikel terkait

Jetpack Compose: Animasi Lanjutan Langkah demi Langkah
Panduan lengkap animasi lanjutan di Compose: transisi, AnimatedVisibility, Animatable, gestur, dan performa untuk antarmuka Android yang halus.

Kotlin Flow vs StateFlow vs SharedFlow: Pertanyaan Interview Android 2026
Pertanyaan Kotlin Flow vs StateFlow vs SharedFlow yang diajukan pewawancara Android pada 2026, dengan jawaban jelas, tabel perbandingan, dan kode siap produksi.

Kotlin 2.3 untuk Android: Name-Based Destructuring, KMP dan Pertanyaan Wawancara 2026
Pertanyaan wawancara Kotlin 2.3 untuk developer Android di tahun 2026. Name-based destructuring, KMP, context parameters, Flow, dan coroutines dengan contoh kode.