Compose Mental Model & Phases Quiz
UI › Compose Core
A state value is read inside a Canvas's drawRect call (the drawing lambda). When that state changes, which phases re-run?
- Composition, layout, and drawing
- Only the drawing phase
- Layout and drawing
- Only the composition phase
Answer: Only the drawing phase
A state read in the drawing phase restarts only drawing; composition and layout are untouched because the UI tree and sizes/positions don't change.
Which statement about recomposition is correct?
- Composables always execute top-to-bottom in source order
- When a parent recomposes, all of its children must recompose
- Compose skips composables whose stable inputs haven't changed
- Recomposition runs on a background thread by default in current Compose
Answer: Compose skips composables whose stable inputs haven't changed
Compose skips a composable when its inputs are stable and unchanged (compared via equals). Execution order isn't guaranteed, parents don't force children, and recomposition runs on the main thread today.
Why does Compose require composable functions to be idempotent and side-effect free?
- Because composables are guaranteed to run only once per full screen render
- Because the Compose compiler forbids declaring any local variables inside them
- Because recomposition can skip, restart, run often, and may run in parallel
- Because composables return a View object that must remain fully immutable
Answer: Because recomposition can skip, restart, run often, and may run in parallel
Recomposition may be skipped, cancelled and restarted, run every frame, and is designed to support parallelism, so side effects inside composables could be dropped, duplicated, or partially applied.
In a for-loop emitting MovieOverview(movie) from the same call site without keys, what happens when a new item is inserted at the top?
- Nothing recomposes at all because the passed-in parameters remain equal
- Subsequent items recompose because their positional identity shifts
- The entire Column is torn down and rebuilt from scratch on each insert
- Compose throws a runtime error demanding an explicit key block be added
Answer: Subsequent items recompose because their positional identity shifts
Without keys, instances are identified by execution order, so inserting at the top shifts every subsequent item's positional identity and forces them to recompose.
Which scenario is the correct way to optimize an Image that offsets based on LazyColumn scroll offset?
- Read the scroll offset inside Modifier.offset { }.
- Read the scroll offset in composition, then pass it to Modifier.offset(dp)
- Wrap the Image in remember to cache the offset value across scrolls
- Mark the Image composable @NonSkippableComposable to skip its recomposition
Answer: Read the scroll offset inside Modifier.offset { }.
The lambda offset { } reads state during the layout (placement) phase, so each scroll re-runs only layout and drawing, skipping recomposition; the dp overload reads in composition and recomposes on every scroll.
What determines a composable's identity in the Composition?
- The hash code of its parameters, which changes whenever values do.
- A globally unique ID assigned at runtime by the Compose engine.
- The order in which composables finish executing after their work.
- Its call site, plus execution order or an explicit key()
Answer: Its call site, plus execution order or an explicit key()
Compose uses the call site (source location) to identify instances; repeated calls from one call site are disambiguated by execution order, or by key() when you provide explicit identity.
Which type would the Compose compiler NOT automatically treat as stable?
- A regular class with a public var of a mutable, non-notifying type
- Compose's own observable MutableState<T> snapshot-backed holder type
- A plain immutable String value that never changes after it is created
- A primitive Int, which is inherently immutable and therefore stable
Answer: A regular class with a public var of a mutable, non-notifying type
Primitives, String, and MutableState are inferred stable. A class with a mutable public property that doesn't notify Compose of changes breaks the stability contract, so it's treated as unstable.
Compose's programming model is intentionally designed so that one phase may potentially run in parallel across multiple cores. Which phase, and why is that allowed?
- Drawing, because Canvas commands are inherently thread-safe and parallel
- Layout, because measuring a node never depends on its siblings or parent
- Composition, because composables must be side-effect free and idempotent
- All three phases always run together on a background thread pool
Answer: Composition, because composables must be side-effect free and idempotent
Because composables must be side-effect free and idempotent, Compose is free to run composition for independent subtrees in parallel; layout and drawing currently execute on the main thread.
You derive showScrollToTop = listState.firstVisibleItemIndex > 0 from a LazyColumn. What keeps the composable from recomposing on every single scroll pixel while still updating the boolean?
- Wrapping the calculation in derivedStateOf { ... }
- Wrapping the index in remember with no keys
- Annotating the composable with @NonRestartableComposable
- Storing the index with rememberSaveable
Answer: Wrapping the calculation in derivedStateOf { ... }
derivedStateOf only notifies readers when its computed result actually changes, so recomposition fires when the boolean flips, not on every change of the frequently-updating firstVisibleItemIndex.
What role does remember play in the Compose mental model?
- It shifts a state read into the layout phase instead of composition.
- It stores a slot-table value so it survives recomposition only.
- It tags the wrapped object stable so the composable can be skipped.
- It makes the enclosing composable recompose on every single frame.
Answer: It stores a slot-table value so it survives recomposition only.
remember stores a value keyed by call site in the slot table and returns it on later recompositions; it is discarded when the composable leaves composition and does not survive configuration changes, for which rememberSaveable is used.
What does annotating a type with @Immutable promise the Compose compiler?
- That every single property of the type is declared private and hidden from callers
- That its public properties never change after construction, so it is stable
- That instances can be serialized and restored across process death automatically
- That the type's equals() method has been overridden to always return true here
Answer: That its public properties never change after construction, so it is stable
@Immutable is a developer promise that the type's public properties are fixed after construction, so Compose can infer it as stable and skip recomposition when the same instance is passed again.
Which statement about the Compose layout phase is correct?
- Layout re-runs the whole composition again to obtain the sizes of its children
- The layout phase always executes on a dedicated background worker thread
- Layout is the phase where Compose decides which composables to actually emit
- Each node is measured once per layout pass, enabling single-pass layout
Answer: Each node is measured once per layout pass, enabling single-pass layout
Compose enforces single-pass measurement, where a parent measures each child once per pass (intrinsics are the explicit escape hatch); deciding what to emit happens in composition, not layout.
In Compose, the guidance 'state flows down, events flow up' primarily describes which idea?
- The fixed order of composition, layout, and draw phases
- How recomposition bubbles upward from a child to its parent
- Unidirectional data flow through state hoisting with callbacks
- How the slot table cleans up nodes after they leave composition
Answer: Unidirectional data flow through state hoisting with callbacks
State hoisting lifts state to a caller that passes the value down and receives updates via callback lambdas, yielding a single source of truth and unidirectional data flow rather than mutable two-way binding.