Lesson 1
Applier operations are a tree protocol
Read traversal, insertion, removal, movement, clearing, and change-list application as one target-tree protocol.
Status: The target-tree contract is Durable . The operation classes and execution order below are Version-specific at the pinned snapshot.
Outcome
Given a pending change, identify which object owns the mutation, where current points, and why a change list is not the UI tree.
current is a traversal cursor
An Applier<N> owns operations against a target tree. down(child) moves the cursor into a child; up() returns to its parent. insertTopDown, insertBottomUp, remove, move, and clear mutate children of the current node. apply(block, value) invokes a property block on current; the runtime does not know whether that property means text, a measure policy, or something else.
AbstractApplier supplies the stack behind current. Its clear() empties the stack, restores the root cursor, and delegates root cleanup to onClear() (Applier.kt). The pinned AbstractApplierTest checks nested down/up, root restoration after clear, multi-item removal, and the move-index rule (tests). A custom applier therefore has two jobs: maintain cursor invariants and mutate its own child representation.
A ChangeList is deferred work
ControlledComposition.recompose() calculates changes; applyChanges() is the boundary that performs them (Composition.kt). At the pinned implementation, the gap-buffer ChangeList stores operations and executes them against slot storage plus an applier (ChangeList.kt). The list is an intermediate program: it can contain UpdateNode, RemoveNode, MoveNode, traversal, and insertion fixups (Operation.kt).
Application brackets execution with onBeginChanges() and onEndChanges(). Remembered callbacks and side effects dispatch after the operation execution (Composition.kt). That is why “the composable ran” does not mean “the target tree changed.” The pinned contract says an application exception irreparably damages the composition, which should then be disposed (Composition.kt).
Choose one insertion direction
For a normal node insertion, the applier must implement either top-down or bottom-up insertion, not both. Top-down inserts a parent before its children; bottom-up builds children first. Choose the direction that avoids repeated ancestor or descendant notifications for your tree (Applier.kt). A special movable-content copy path is outside this normal insertion contract.
Reproduce the index invariant
At the pinned AndroidX checkout, run:
./gradlew :compose:runtime:runtime:desktopTest \
--tests 'androidx.compose.runtime.AbstractApplierTest'
Controls: exercise move(1, 3, 1) on A B C D E, then remove one and several children. Expected: the final child list follows the pre-change to index rule, and clear() leaves current === root. Limit: this tests AbstractApplier helpers, not a Compose UI frame or every ChangeList ordering. Not executed in this production run.
Misconceptions
- “A change list is already the rendered tree.” It is deferred operations plus slot-storage fixups.
- “
move(from, to, count)uses the final destination index.”tois relative to the list before the change. - “A recomposition replaces every child.” The list can update, remove, or move existing nodes.
Check yourself
Before applyChanges(), which can already be different: composition memory, the pending operation list, or the applier’s target tree? Explain why.
Evidence ledger
| Claim | Direct evidence | Status |
|---|---|---|
Applier owns traversal and target-tree operations | Applier.kt | Durable |
Composition calculates before applyChanges executes | Composition.kt | Durable
Version-specific |
Gap-buffer ChangeList executes operations against an applier | ChangeList.kt | Version-specific |
AbstractApplier preserves cursor and list-helper behavior | Applier.kt and AbstractApplierTest.kt | Durable
Version-specific |
Freshness
Refresh when Changes.execute, operation fixups, applier cursor semantics, or the applyChanges error contract changes. Do not infer UI measurement or drawing from this runtime-only lesson.
Finished this lesson?
Your progress stays only in this browser.