Android with Jetpack Compose
Pathrule2 Rules • 3 Memories • 1 Skill
Jetpack Compose redraws from state while Android can recreate activities, stop collectors, kill the process, or resume a task after configuration and navigation changes, so correct code must distinguish composition lifetime from screen and process lifetime. This pattern constrains immutable UI state and effect ownership, records ViewModel, persistence, navigation, and coroutine decisions, and adds a lifecycle-focused review for screens and background transitions. It differs from Flutter and Expo patterns by focusing on Android lifecycle owners, saved state, coroutines, WorkManager, Compose effect APIs, and Gradle-driven native application structure.
Suggested path map
Pathrule places each piece on the matching path, so your assistant only sees it where it belongs. This is the scoping you get on import; you can adjust it in your workspace.
Rules
2Render immutable state and emit events upward/app/src/main/javahighstrictComposable screens receive immutable state and callbacks, while ViewModels own transitions and expose a single observable UI state.
| 1 | Recomposition can happen at any time and in any order allowed by the runtime. A composable that mutates shared objects while rendering or reaches into repositories directly makes repeated execution produce new side effects. |
| 2 | |
| 3 | - Expose one immutable screen state from the ViewModel and derive display-only values with stable calculations rather than maintaining parallel booleans that can contradict each other. |
| 4 | - Pass state down and events up. Keep navigation, repository calls, and domain transitions behind event handlers owned by the screen or ViewModel. |
| 5 | - Use stable keys for lazy lists and do not mutate collection instances in place; replace state with a new value so Compose can observe the transition. |
| 6 | - Hoist reusable component state to the lowest common owner, but keep transient visual details local when no other component or process needs them. |
| 7 | |
| 8 | See /app/src/test for the adjacent decision or procedure that completes this constraint. |
Launch effects only through lifecycle-aware APIs/app/src/main/javahighstrictTie suspend work, listeners, and flow collection to explicit Compose and Android lifecycles instead of starting them during rendering.
| 1 | A composable body is not a lifecycle callback. Starting a coroutine, registering a listener, or sending analytics directly during composition repeats work whenever the function is recomposed. |
| 2 | |
| 3 | - Use `LaunchedEffect` with keys that describe when work must restart, and keep changing callbacks current without restarting long-lived work unnecessarily. |
| 4 | - Register disposable listeners in `DisposableEffect` and remove the exact listener in cleanup. Do not rely on activity destruction to clean a screen-level resource. |
| 5 | - Collect flows with lifecycle-aware collection so stopped screens do not continue expensive work or mutate invisible UI state. |
| 6 | - Use `rememberCoroutineScope` only for jobs triggered by UI events and expected to end with the current composition; durable work belongs in a ViewModel or WorkManager. |
| 7 | |
| 8 | See /app/src/main/java for the adjacent decision or procedure that completes this constraint. |
Memories
3remember, saved state, and persistence solve different lifetimes/app/src/main/javaChoose state storage by whether it must survive recomposition, configuration change, process recreation, or application restart.
| 1 | Android has several independent loss boundaries. Treating `remember` as persistence works in a preview and fails when the device rotates, the process is reclaimed, or the user returns after hours. |
| 2 | |
| 3 | - Use `remember` for state that only needs to survive recomposition inside the current composition instance. |
| 4 | - Use saveable state for small UI values that can be represented in a bundle and should return after recreation; do not place large objects or active resources there. |
| 5 | - Keep screen state and asynchronous work in a ViewModel so configuration changes do not restart the operation. |
| 6 | - Persist durable user or domain data in a database or other repository. Saved state is a reconstruction hint, not a replacement for durable storage. |
| 7 | |
| 8 | See /app/src/test for the rule or workflow that puts this decision into practice. |
Durable background work belongs to WorkManager/app/src/main/javaUse WorkManager for deferrable work that must survive process death; use foreground or in-process work only for their actual lifetime guarantees.
| 1 | A coroutine launched from an activity or ViewModel ends when its owner is cleared or the process disappears. That is correct for UI work but incorrect for a required upload, synchronization, or retry that must eventually complete. |
| 2 | |
| 3 | - Schedule deferrable guaranteed work with constraints and an idempotent worker whose input is a stable identifier, not a large object snapshot. |
| 4 | - Use unique work and an explicit replacement or keep policy when repeated user actions refer to the same logical operation. |
| 5 | - Report progress through durable state the UI can observe after recreation instead of holding a callback to the original screen. |
| 6 | - Use a foreground service only when the user-visible ongoing task and platform policy require it; it is not a generic escape hatch for unlimited background execution. |
| 7 | |
| 8 | See /app/src/test for the rule or workflow that puts this decision into practice. |
Skills
1review-compose-lifecycles/rootExercise recomposition, navigation, configuration, process recreation, and background transitions for a Compose feature.
| 1 | --- |
| 2 | name: review-compose-lifecycles |
| 3 | description: Review a Jetpack Compose feature after changes to state, effects, navigation, or background work. |
| 4 | --- |
| 5 | |
| 6 | # Review Compose Lifecycles |
| 7 | |
| 8 | Run this procedure when the affected surface changes, before the result is promoted to production. Record evidence for every step instead of accepting a plausible-looking result. |
| 9 | |
| 10 | - [ ] Enable recomposition diagnostics and verify rendering does not start network calls, analytics, listeners, or mutations more than the intended number of times. |
| 11 | - [ ] Navigate away and back while effects and collectors are active; confirm cleanup occurs and state ownership matches the screen lifecycle. |
| 12 | - [ ] Recreate the activity and restore from saved state, then simulate process recreation and verify durable data reloads from the repository. |
| 13 | - [ ] Open the destination through both in-app navigation and a deep link with valid, missing, stale, and unauthorized identifiers. |
| 14 | - [ ] Interrupt each background operation, relaunch the app, and prove WorkManager or the chosen owner resumes idempotently without duplicating external effects. |
| 15 | |
| 16 | ## Exit criteria |
| 17 | |
| 18 | The change is complete only when the expected behavior, failure behavior, and rollback path have all been exercised with representative data. Preserve the evidence with the change so the next operator can repeat the same checks. |
Why this pattern
AI agents often launch work during recomposition, store durable state only with remember, collect flows beyond the visible lifecycle, or pass whole mutable objects through navigation routes.
Built for Android teams building Kotlin applications with Jetpack Compose, coroutines, and architecture components.
Keeps your assistant from:
- Repeating network or analytics work during recomposition
- Losing user progress after process recreation
- Collecting a hot flow while the screen is stopped
- Passing mutable domain objects through navigation arguments
- License
- Apache-2.0
- Version
- 1.0.0
- Updated
- 2026-08-25