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

codeTap to expand ⛶
1// VIEW SYSTEM — How measure/layout/draw works internally
2// Each View goes through 3 phases per frame:
3
4// 1. Measure: determine size
5// parent calls child.measure(widthMeasureSpec, heightMeasureSpec)
6// child sets measuredWidth/measuredHeight via setMeasuredDimension()
7
8// 2. Layout: determine position
9// parent calls child.layout(left, top, right, bottom)
10
11// 3. Draw: render pixels
12// canvas operations in onDraw(canvas)
13
14// Custom View example (View system)
15class ProgressRing(context: Context, attrs: AttributeSet?) : View(context, attrs) {
16 private var progress = 0f
17 private val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
18 style = Paint.Style.STROKE
19 strokeWidth = 12f
20 color = Color.BLUE
21 }
22
23 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 }
27
28 fun setProgress(value: Float) {
29 progress = value.coerceIn(0f, 1f)
30 invalidate() // Trigger redraw
31 }
32}
33
34// JETPACK COMPOSE — Same component, declarative
35@Composable
36fun 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 changes
47}
48
49// Compose recomposition — how it works
50@Composable
51fun Counter() {
52 var count by remember { mutableStateOf(0) }
53 // When count changes, ONLY this composable re-executes
54 // Compose's snapshot system detects the read of 'count' during composition
55 // and schedules recomposition of this scope when 'count' is written to
56
57 Column {
58 Text("Count: $count") // Re-composed when count changes
59 Button(onClick = { count++ }) {
60 Text("Increment") // NOT re-composed (count not read here)
61 }
62 }
63}

🏋️ Practice Exercise

Practice:

  1. Implement the same UI in both XML+Views and Compose — compare the code
  2. Explain how Compose's recomposition skipping works with stable types
  3. What is the Slot Table and how does Compose use it internally?
  4. Create a custom layout in Compose using the Layout composable
  5. Explain what remember and rememberSaveable do — how do they differ?
  6. What are the performance implications of using unstable lambdas in Compose?
  7. 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.