Deconstructing the Compose Compiler: How @Composable Changes Android Development
@Composable: More Than Just an Annotation
Many beginners think `@Composable` is similar to Dagger's `@Inject`, a runtime annotation. In fact, it's more like Kotlin's `suspend` keyword, a language-level feature that changes the function type.
`@Composable` corresponds to a Kotlin compiler plugin (Compose Compiler Plugin). During compilation, this plugin intercepts all marked functions and rewrites their signatures. The most notable change is injecting an implicit parameter: `Composer`.
`Composer` is the core context object of Compose runtime, running through the entire UI tree construction process, responsible for recording the position of executing nodes, storing state, and scheduling recomposition. This explains why Composable functions can only be called from other Composable functions — they need this implicitly passed `Composer` context.
Slot Table and Gap Buffer
How does Compose store the UI tree? It doesn't use traditional object trees (like View Hierarchy) but an array-based linear data structure called "Slot Table."
The Slot Table's design is inspired by "Gap Buffer" commonly used in text editors. It's an array containing data but keeps an empty "gap" at the current operation position. This allows Compose to move the cursor in O(1) time complexity and efficiently insert or delete data at the current position.
This structure is extremely suitable for the dynamic nature of UI. When recomposition occurs, code generated by the Compose compiler accesses the slot table in execution order. If data (such as state or node structure) hasn't changed, it skips directly; if changed, it efficiently updates array content using the gap buffer.
Positional Memoization
Understanding the slot table helps understand how `remember` works. `remember` isn't magic; it uses "Positional Memoization" technique.
Because Composable function execution order is typically stable during recomposition, Compose can use the function's "position" in source code as a key to look up and store values in the slot table. When code executes `remember { ... }`, Composer checks if a cached value already exists at the current slot position. If yes and dependencies haven't changed, return directly; otherwise, execute the lambda to compute new value and store in slot.
This mechanism allows functional UI to have "state," and this state can persist across multiple recompositions until that Composable is removed from the UI tree.