View System vs Jetpack Compose (Deep Comparison)
📖 Concept
The View system (XML + View classes) has been Android's UI framework since day one. Jetpack Compose is the modern, declarative UI toolkit released in 2021. At the senior level, you must understand both deeply and when to choose each.
View System (Imperative):
- UI defined in XML, inflated into View objects at runtime
- Three-phase rendering: Measure → Layout → Draw (traversal of the View tree)
- Each View is a Java/Kotlin object with mutable state
- Updates are imperative:
textView.text = "Hello"
Jetpack Compose (Declarative):
- UI defined as Kotlin functions annotated with @Composable
- Uses a Slot Table (gap buffer) to store the composition tree
- Recomposition: when state changes, only affected composables re-execute
- Positional memoization: Compose identifies composables by their call site position
Internal comparison:
| Aspect | View System | Jetpack Compose |
|---|---|---|
| Rendering | Measure/Layout/Draw on View tree | Composition → Layout → Drawing on Compose nodes |
| State | Mutable View objects | Immutable state, recomposition |
| Memory | Each View = Java object (~200+ bytes) | Slot table entries (~50-80 bytes per node) |
| Updates | Imperative (set properties) | Declarative (re-run function) |
| Deep nesting | Can cause perf issues (measure passes) | Intrinsic measurements solve this |
When to use which:
- New projects: Compose (Google's recommended approach)
- Existing large apps: Incremental adoption via ComposeView in XML layouts
- Custom drawing: Both work, but Compose's Canvas API is simpler
- Complex animations: Compose's animation APIs are significantly easier
💻 Code Example
1// VIEW SYSTEM — How measure/layout/draw works internally2// Each View goes through 3 phases per frame:34// 1. Measure: determine size5// parent calls child.measure(widthMeasureSpec, heightMeasureSpec)6// child sets measuredWidth/measuredHeight via setMeasuredDimension()78// 2. Layout: determine position9// parent calls child.layout(left, top, right, bottom)1011// 3. Draw: render pixels12// canvas operations in onDraw(canvas)1314// Custom View example (View system)15class ProgressRing(context: Context, attrs: AttributeSet?) : View(context, attrs) {16 private var progress = 0f17 private val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {18 style = Paint.Style.STROKE19 strokeWidth = 12f20 color = Color.BLUE21 }2223 override fun onDraw(canvas: Canvas) {24 val rect = RectF(12f, 12f, width - 12f, height - 12f)25 canvas.drawArc(rect, -90f, 360f * progress, false, paint)26 }2728 fun setProgress(value: Float) {29 progress = value.coerceIn(0f, 1f)30 invalidate() // Trigger redraw31 }32}3334// JETPACK COMPOSE — Same component, declarative35@Composable36fun ProgressRing(progress: Float, modifier: Modifier = Modifier) {37 Canvas(modifier = modifier.size(48.dp)) {38 drawArc(39 color = Color.Blue,40 startAngle = -90f,41 sweepAngle = 360f * progress,42 useCenter = false,43 style = Stroke(width = 4.dp.toPx())44 )45 }46 // No invalidate() needed — recomposes automatically when progress changes47}4849// Compose recomposition — how it works50@Composable51fun Counter() {52 var count by remember { mutableStateOf(0) }53 // When count changes, ONLY this composable re-executes54 // Compose's snapshot system detects the read of 'count' during composition55 // and schedules recomposition of this scope when 'count' is written to5657 Column {58 Text("Count: $count") // Re-composed when count changes59 Button(onClick = { count++ }) {60 Text("Increment") // NOT re-composed (count not read here)61 }62 }63}
🏋️ Practice Exercise
Practice:
- Implement the same UI in both XML+Views and Compose — compare the code
- Explain how Compose's recomposition skipping works with stable types
- What is the Slot Table and how does Compose use it internally?
- Create a custom layout in Compose using the Layout composable
- Explain what
rememberandrememberSaveabledo — how do they differ? - What are the performance implications of using unstable lambdas in Compose?
- How does Compose interop work — ComposeView in XML and AndroidView in Compose?
⚠️ Common Mistakes
Deeply nesting Views (XML) without considering measure pass performance — each nested ViewGroup can trigger multiple measure passes
Not understanding recomposition in Compose — putting side effects or heavy computation directly in composable functions
Using mutableStateOf for objects that are not stable — causes unnecessary recompositions
Not using remember for expensive computations in Compose — recalculated on every recomposition
Mixing View system patterns with Compose — e.g., trying to hold a reference to a Compose element and mutate it
💼 Interview Questions
🎤 Mock Interview
Mock interview is powered by AI for View System vs Jetpack Compose (Deep Comparison). Login to unlock this feature.