{
  "id": "kotlin-compose-android",
  "name": "Native Android: Kotlin + Jetpack Compose",
  "use_cases": [
    "Android phones, tablets and foldables",
    "Native Android integrations",
    "Accessible mobile clients for a shared service"
  ],
  "choose_when": [
    "Android-native UI and platform behavior are requirements",
    "The team can own an Android SDK, emulator/device and signed release pipeline"
  ],
  "avoid_when": [
    "The requirement is a single shared UI for Android and iOS",
    "No Android device or emulator verification can be performed"
  ],
  "tools": [
    {
      "job": "UI and state",
      "default": "Jetpack Compose + Material 3; screen ViewModels exposing immutable StateFlow with lifecycle-aware collection",
      "alternatives": [
        "Android Views for existing apps or unsupported controls"
      ],
      "reason": "Unidirectional data flow separates user events and rendered state; repositories own data access",
      "limits": "ViewModels do not survive process death; persist important state and test restoration; add domain layers only for actual complexity",
      "source_urls": [
        "https://developer.android.com/topic/architecture/recommendations"
      ]
    },
    {
      "job": "Structured local persistence",
      "default": "Room",
      "alternatives": [
        "Direct SQLite only when Room cannot satisfy measured requirements"
      ],
      "reason": "Compile-time SQL checking, migrations and observable local data",
      "limits": "Choose compatible Room/KSP/Kotlin versions; test upgrades; neither Room nor DataStore automatically supplies encryption or backend sync",
      "source_urls": [
        "https://developer.android.com/training/data-storage/room",
        "https://developer.android.com/topic/libraries/architecture/datastore"
      ]
    },
    {
      "job": "Preferences",
      "default": "DataStore",
      "alternatives": [
        "Existing supported preferences implementation with an explicit data migration"
      ],
      "reason": "Compile-time SQL checking, migrations and observable local data",
      "limits": "Choose compatible Room/KSP/Kotlin versions; test upgrades; neither Room nor DataStore automatically supplies encryption or backend sync",
      "source_urls": [
        "https://developer.android.com/training/data-storage/room",
        "https://developer.android.com/topic/libraries/architecture/datastore"
      ]
    },
    {
      "job": "Networking and API contract",
      "default": "Retrofit + OkHttp + kotlinx.serialization behind a repository",
      "alternatives": [
        "Ktor Client when deliberate multiplatform business-code sharing justifies it"
      ],
      "reason": "Typed HTTP interfaces with explicit DTOs and a replaceable client",
      "limits": "Pin compatible versions and converter; share schemas/fixtures with iOS rather than UI; test auth refresh, errors and cancellation",
      "source_urls": [
        "https://github.com/lysine-dev/retrofit"
      ]
    },
    {
      "job": "Concurrency and background work",
      "default": "Coroutines/Flow in lifecycle-owned scopes; WorkManager for eligible persistent deferrable work",
      "alternatives": [
        "Server-side durable jobs for long agent runs"
      ],
      "reason": "Cancellation follows lifecycle while scheduled local sync uses a persistent work system",
      "limits": "WorkManager is OS-constrained and not an exact scheduler; coroutine launch alone is not durable across process death",
      "source_urls": [
        "https://developer.android.com/topic/architecture/recommendations",
        "https://developer.android.com/develop/background-work/background-tasks/persistent"
      ]
    },
    {
      "job": "Credentials",
      "default": "Android Keystore-backed cryptographic keys protecting app-private credential material",
      "alternatives": [
        "In-memory sessions when no persisted credentials are needed"
      ],
      "reason": "Use OS key protection and explicitly scoped encrypted storage",
      "limits": "Keystore stores keys, not arbitrary bearer tokens; define encryption, backup exclusions and revocation; no backend provider secrets in APKs",
      "source_urls": [
        "https://developer.android.com/privacy-and-security/keystore"
      ]
    },
    {
      "job": "Unit and integration tests",
      "default": "JUnit + coroutine test utilities; fake repositories/API; Room migration tests",
      "alternatives": [
        "Established project test framework when compatible"
      ],
      "reason": "Deterministically verify state, retries, decoding, migrations and process-restoration assumptions",
      "limits": "Host unit tests cannot prove Android framework/device behavior; emulator tests supplement them",
      "source_urls": [
        "https://developer.android.com/topic/architecture/recommendations",
        "https://developer.android.com/training/data-storage/room"
      ]
    },
    {
      "job": "UI and accessibility",
      "default": "Compose UI tests + AndroidX accessibility checks; TalkBack and large-font/device review",
      "alternatives": [
        "Espresso for mixed Views/Compose apps"
      ],
      "reason": "Verify semantic actions, navigation and permission flows with the actual platform UI",
      "limits": "Automatic checks require supported test dependencies/API environment and do not replace screen-reader usability; test tablets/foldables if supported",
      "source_urls": [
        "https://developer.android.com/develop/ui/compose/testing",
        "https://developer.android.com/develop/ui/compose/accessibility/testing"
      ]
    },
    {
      "job": "Build, CI and distribution",
      "default": "Android Studio + Gradle wrapper/Kotlin DSL; pinned JDK/SDK CI; signed Android App Bundle and Play internal testing",
      "alternatives": [
        "Managed device or device-farm testing where the project needs a broader matrix"
      ],
      "reason": "Reproduce build inputs and separate local tests, device tests and signed release artifacts",
      "limits": "AGP/Gradle/JDK/Kotlin/Compose must be compatible; release requires upload-key custody and Play account access; build success is not store publication",
      "source_urls": [
        "https://developer.android.com/build/building-cmdline",
        "https://developer.android.com/studio/publish/app-signing"
      ]
    },
    {
      "job": "Navigation",
      "default": "Navigation Compose",
      "alternatives": [
        "Preserve an existing compatible project choice"
      ],
      "reason": "Navigation host/controller handle screen back stacks and deep links",
      "limits": "Add compatible navigation-compose dependency; pass minimal route identifiers and test process recreation/back behavior. Preserve Fragment navigation during partial Compose migration.",
      "source_urls": [
        "https://developer.android.com/guide/navigation"
      ]
    },
    {
      "job": "Notifications",
      "default": "Android notification channels + runtime permission",
      "alternatives": [
        "Preserve an existing compatible project choice"
      ],
      "reason": "Native notifications keep user delivery preferences under system control",
      "limits": "Declare/request POST_NOTIFICATIONS where required; create appropriate channels, handle denial and test release API levels. Remote transport/server enrollment is a separate integration.",
      "source_urls": [
        "https://developer.android.com/develop/ui/compose/notifications/notification-permission"
      ]
    },
    {
      "job": "Media selection",
      "default": "Android Photo Picker",
      "alternatives": [
        "Preserve an existing compatible project choice"
      ],
      "reason": "System picker grants access to selected media",
      "limits": "Use Activity result contracts; handle fallback devices, URI access lifetime and upload size limits. Prefer selected access over requesting broad media-library permissions.",
      "source_urls": [
        "https://developer.android.com/training/data-storage/shared/photo-picker"
      ]
    }
  ],
  "setup_steps": [
    "Start in a source checkout in a local development workspace. Record package ID, minimum/target SDK, supported form factors, API contract, permissions and release owner.",
    "Install Android Studio and compatible SDK/JDK; use the project Gradle wrapper. Create a Kotlin Empty Activity/Compose project. Pin Gradle/AGP/Kotlin/Compose and library versions in a version catalog.",
    "Implement screen state/ViewModels and repositories. Add Room migrations, a typed HTTP client and deterministic fake data. Keep secrets out of version control and app resources.",
    "Run JVM tests, lint and a debug build. Start a documented emulator or attach an authorized device, then run instrumentation/Compose checks.",
    "Exercise process death, offline/reconnect, rotation, permission denial, TalkBack, large fonts and each supported form factor on appropriate devices.",
    "Account owner establishes package ownership, upload key and Play App Signing. Build a signed release bundle, verify internal-track install/update, then use the authorized release workflow; retain reports and distribution receipts."
  ],
  "verification_commands": [
    "./gradlew --version",
    "./gradlew tasks --all",
    "./gradlew :app:lintDebug :app:testDebugUnitTest :app:assembleDebug",
    "adb devices",
    "./gradlew :app:connectedDebugAndroidTest",
    "./gradlew :app:bundleRelease"
  ],
  "evidence_status": "documentation-reviewed",
  "sources": [
    {
      "url": "https://developer.android.com/topic/architecture/recommendations",
      "title": "Android architecture recommendations",
      "checked_date": "2026-10-10"
    },
    {
      "url": "https://developer.android.com/training/data-storage/room",
      "title": "Room persistence",
      "checked_date": "2026-10-10"
    },
    {
      "url": "https://developer.android.com/topic/libraries/architecture/datastore",
      "title": "DataStore",
      "checked_date": "2026-10-10"
    },
    {
      "url": "https://github.com/lysine-dev/retrofit",
      "title": "Retrofit maintained repository; square/retrofit redirects here",
      "checked_date": "2026-10-10"
    },
    {
      "url": "https://developer.android.com/develop/background-work/background-tasks/persistent",
      "title": "Persistent background work",
      "checked_date": "2026-10-10"
    },
    {
      "url": "https://developer.android.com/develop/ui/compose/testing",
      "title": "Compose testing",
      "checked_date": "2026-10-10"
    },
    {
      "url": "https://developer.android.com/develop/ui/compose/accessibility/testing",
      "title": "Compose accessibility testing",
      "checked_date": "2026-10-10"
    },
    {
      "url": "https://developer.android.com/build/building-cmdline",
      "title": "Android command-line build",
      "checked_date": "2026-10-10"
    },
    {
      "url": "https://developer.android.com/studio/publish/app-signing",
      "title": "Android app signing",
      "checked_date": "2026-10-10"
    },
    {
      "url": "https://developer.android.com/privacy-and-security/keystore",
      "title": "Android Keystore system",
      "checked_date": "2026-10-10"
    },
    {
      "url": "https://developer.android.com/guide/navigation",
      "title": "Android Navigation",
      "checked_date": "2026-10-10"
    },
    {
      "url": "https://developer.android.com/develop/ui/compose/notifications/notification-permission",
      "title": "Android notification permission",
      "checked_date": "2026-10-10"
    },
    {
      "url": "https://developer.android.com/training/data-storage/shared/photo-picker",
      "title": "Android Photo Picker",
      "checked_date": "2026-10-10"
    }
  ],
  "open_questions": [
    "What minimum/target API and phone/tablet/foldable matrix must be supported?",
    "Which offline/sync/conflict rules and API authentication contract apply?",
    "Which notification/media features and denied-permission flows are required?",
    "Who owns package identity, upload key and Play internal/release tracks?"
  ],
  "title": "Kotlin + Compose",
  "language": "Kotlin",
  "group": "Native mobile",
  "kind": [
    "android"
  ],
  "ui": "native",
  "host": "native",
  "summary": "Native Android apps with platform UI, lifecycle and storage integration.",
  "shape": "Compose · ViewModel · Room · Coroutines",
  "tradeoff": "Device diversity and background restrictions need Android-specific checks; iOS is a separate client.",
  "research_file": "native-mobile.json",
  "reviewed": "2026-10-10",
  "best_practices": [
    "Expose immutable screen state from ViewModels and collect it with lifecycle awareness.",
    "Use lifecycle-owned coroutine scopes. Use WorkManager for eligible persistent, deferrable work.",
    "Test Room migrations and keep credential material protected by platform-backed keys.",
    "Check TalkBack, large text and real devices, then qualify the signed bundle through internal testing."
  ],
  "research_freshness": {
    "core_guidance_checked_on": "2026-10-13",
    "sources": [
      "https://developer.android.com/compose",
      "https://developer.android.com/topic/architecture"
    ],
    "scope": "Core framework roles, current setup/agent documentation and cited-source availability rechecked; not a reimplementation or exhaustive semantic audit of every third-party dependency."
  },
  "guide_url": "/stacks/kotlin-compose-android/",
  "markdown_url": "/stacks/kotlin-compose-android/guide.md",
  "json_url": "/stacks/kotlin-compose-android/record.json",
  "architecture_and_migration": {
    "id": "kotlin-compose-android",
    "version_scope": "Documentation reviewed 11 October 2026. Use the project's minimum/target SDK, compatible Kotlin/Compose/AGP/Gradle/JDK/Room/WorkManager versions and version catalog. Current guidance is conditional on supported platform behavior; no APK, App Bundle, emulator or store release was tested.",
    "operating_model": "An installed Android client renders immutable screen state with lifecycle-aware collection and persists important data separately. Recommendation: repositories mediate Room/API data and WorkManager handles selected persistent work; backend services own privileged domain effects. Installed versions coexist through staged distribution.",
    "architecture": [
      "Android guidance recommends separate UI and data layers, repositories, unidirectional data flow and lifecycle-aware Flow collection. Add a domain layer only when complexity/reuse warrants it.",
      "Recommendation: keep API DTOs distinct from Room entities, and local observable data authoritative for offline screen reads while the backend owns shared-domain reconciliation.",
      "Room owns schema versions and migrations; DataStore serves preferences rather than a general relational substitute. Record durable user operations and acknowledgement state when offline writes are required.",
      "Coroutines serve lifecycle/in-process async work; WorkManager persists scheduled work across restart/reboot and remains subject to constraints and power policy.",
      "Recommendation: use Keystore-backed keys to protect separately stored app-private credentials. A Keystore key is not arbitrary token storage, nor a guarantee that a revoked backend session remains usable."
    ],
    "developer_workflow": [
      "Start at the external source README, API and database fixtures, version catalog and Gradle wrapper. Record package identity, SDK/device matrix, signing owner and release track.",
      "Use ./gradlew --version and ./gradlew tasks --all; with an actual app module run ./gradlew :app:lintDebug :app:testDebugUnitTest :app:assembleDebug. Keep wrapper/JDK/SDK inputs reproducible.",
      "After adb devices identifies an authorized emulator/device, run ./gradlew :app:connectedDebugAndroidTest. Configure Room's migration testing/exported schemas; assertions must check preserved data as well as schema shape.",
      "Recommendation: use Android Studio profilers/inspection, coroutine fakes, Compose navigation/accessibility tests and physical-device reboot/offline checks. Build ./gradlew :app:bundleRelease only with reviewed signing, then verify an internal-track install/update before staged promotion."
    ],
    "avoid": [
      "Collecting Flow without lifecycle ownership can keep work running when a screen is stopped; repeating effects inside recomposition can duplicate requests.",
      "Treating a ViewModel or coroutine as disk persistence loses work on process death.",
      "Using destructive migration for user-created offline data deletes the only copy.",
      "Expecting WorkManager to execute at an exact wall-clock deadline ignores scheduling windows and power constraints.",
      "Halt rollout is not uninstall or downgrade; already-updated users stay on the new build."
    ],
    "transitions": [
      {
        "capability": "client",
        "existing_contract": "Supported installed APK versions/OS levels, DTOs/errors, auth, deep links and delayed old-client operations.",
        "coexistence": "Recommendation: Deploy backward-compatible server behavior first; run old/new clients against it and retain contracts through explicit adoption/support gates.",
        "target": "Recommendation: Version API DTOs independently of Room entities and gate optional features by negotiated capability; keep backend authorization authoritative.",
        "benefits": [
          {
            "benefit": "Client releases and backend changes can progress independently.",
            "prerequisites": "Installed-version inventory, shared API fixtures and support ownership.",
            "tradeoffs": "Maintaining old contracts costs work; Compose adoption does not upgrade installed clients."
          }
        ],
        "checks": [
          "Old/new builds decode missing/unknown fields and documented errors.",
          "A delayed old offline operation succeeds or returns the declared conflict.",
          "Deep links, session expiry and denied resources work across supported releases."
        ],
        "rollback": "Disable the new capability and preserve old/new APIs; repair already-installed clients with a compatible higher-version release when needed.",
        "sources": [
          "https://developer.android.com/topic/architecture/recommendations",
          "https://support.google.com/googleplay/android-developer/answer/6346149"
        ],
        "id": "client-1"
      },
      {
        "capability": "data",
        "existing_contract": "Room schema versions, exported schemas, all upgrade paths and user-owned unsynced records.",
        "coexistence": "Recommendation: Add migrations without discarding old fields until adoption permits; include direct upgrades from every supported starting version.",
        "target": "Recommendation: Keep exported Room schemas and explicit migration tests; use automatic or manual migrations according to the actual change.",
        "benefits": [
          {
            "benefit": "Reviewed migrations preserve local user data across updates.",
            "prerequisites": "Complete paths plus assertions on data/relationships/pending work.",
            "tradeoffs": "Schema validation alone does not establish value preservation; destructive fallback permanently deletes table data."
          }
        ],
        "checks": [
          "MigrationTestHelper exercises each path and earliest-to-current upgrade.",
          "Assert unsynced rows, IDs and relationships remain meaningful.",
          "Verify intended downgrade behavior; prohibit destructive fallback for the only copy of user work."
        ],
        "rollback": "Ship a forward repair when older code cannot read the new store; any restore requires reconciling writes made after upgrade.",
        "sources": [
          "https://developer.android.com/training/data-storage/room/migrating-db-versions"
        ],
        "id": "data-1"
      },
      {
        "capability": "cache",
        "existing_contract": "Local source-of-truth reads, pending offline operations, revision/cursor/tombstone and conflict rules.",
        "coexistence": "Recommendation: Use one repository and one reconciler for each data category; persist queued mutations with operation IDs before reporting success.",
        "target": "Recommendation: Use Room-backed local reads and a durable operation queue when offline writes are needed; select push/pull/hybrid sync and a domain-specific conflict policy.",
        "benefits": [
          {
            "benefit": "Cached local data can keep required reads available and preserve offline intent.",
            "prerequisites": "Server sync support, retry-safe writes and an explicit conflict policy.",
            "tradeoffs": "Offline-first reads do not imply offline writes; last-write-wins can discard intent and depends on metadata semantics."
          }
        ],
        "checks": [
          "Two offline devices make conflicting changes and obtain the documented resolution.",
          "Kill process after local commit and before network send; operation survives.",
          "Repeat server-committed/unacknowledged mutation without duplication; honor deletes/tombstones."
        ],
        "rollback": "Stop the new synchronizer and retain its queue; map remaining operations/cursors to old semantics before resume.",
        "sources": [
          "https://developer.android.com/topic/architecture/data-layer/offline-first"
        ],
        "id": "cache-1"
      },
      {
        "capability": "jobs",
        "existing_contract": "Existing queued Worker class names, input Data versions, unique-work identities and external effects.",
        "coexistence": "Recommendation: Retain readers/worker entry points for already-enqueued payloads; transfer one job category and prevent duplicate schedulers.",
        "target": "Recommendation: Use WorkManager for eligible persistent device work with constraints/backoff; keep server completion obligations in a backend durable job.",
        "benefits": [
          {
            "benefit": "Scheduled work persists across restart and device reboot.",
            "prerequisites": "Compatible worker payloads, stable unique names, replay-safe effects and documented constraints.",
            "tradeoffs": "Scheduling windows/power policy affect timing; unique work does not provide exactly-once remote effects."
          }
        ],
        "checks": [
          "Reboot with queued work and verify it resumes when constraints permit.",
          "Update app with old queued input; the new worker decodes it.",
          "Crash after remote effect/before acknowledgement and confirm retry is safe."
        ],
        "rollback": "Cancel new scheduling and account for pending/running work; keep compatibility workers until drained or safely transformed.",
        "sources": [
          "https://developer.android.com/develop/background-work/background-tasks/persistent",
          "https://developer.android.com/topic/architecture/data-layer/offline-first"
        ],
        "id": "jobs-1"
      },
      {
        "capability": "identity",
        "existing_contract": "Existing stored credential encryption, key alias/access constraints, backup exclusions, issuer expiry/revocation.",
        "coexistence": "Recommendation: Retain the backend issuer and read old encrypted credential formats during a bounded transition; isolate account-local data.",
        "target": "Recommendation: Use Keystore-backed cryptographic keys with reviewed encrypted app-private credential storage and backend revocation.",
        "benefits": [
          {
            "benefit": "OS key protection can reduce key extraction exposure.",
            "prerequisites": "Qualified device/API support, key invalidation handling and a reviewed storage/backup policy.",
            "tradeoffs": "Keystore stores keys rather than arbitrary tokens; device protection does not replace backend authorization."
          }
        ],
        "checks": [
          "Key invalidation produces a controlled sign-in path.",
          "Backup/restore cannot silently expose unusable/plain credentials.",
          "Sign-out/account switch stops old-account queued work and backend revocation is enforced."
        ],
        "rollback": "Retain compatibility decryption only while secure; require sign-in if the old format/key cannot be restored safely.",
        "sources": [
          "https://developer.android.com/privacy-and-security/keystore"
        ],
        "id": "identity-1"
      },
      {
        "capability": "operations",
        "existing_contract": "Play tracks, signing/version codes, adoption, crash metrics and schema/API compatibility.",
        "coexistence": "Recommendation: Verify an internal/closed-track update over supported old builds and preserve compatible backend behavior for the whole mixed-version population.",
        "target": "Recommendation: Use a staged rollout with explicit crash/data-loss thresholds and a higher-version compatible repair path.",
        "benefits": [
          {
            "benefit": "Percentage-based adoption can limit initial exposure.",
            "prerequisites": "Signed build, account/track owner, telemetry and recovery artifact.",
            "tradeoffs": "Halting only prevents further rollout; users who already updated remain on that version."
          }
        ],
        "checks": [
          "Install-over-old-build retains unsynced data and auth.",
          "Track failures by app/OS/schema version.",
          "Halt/recovery rehearsal keeps already-updated users functional."
        ],
        "rollback": "Halt additional rollout and disable the affected capability if safe; publish a compatible fixed build without assuming a downgrade of installed clients.",
        "sources": [
          "https://support.google.com/googleplay/android-developer/answer/6346149"
        ],
        "id": "operations-1"
      }
    ],
    "sources": [
      {
        "url": "https://developer.android.com/topic/architecture/recommendations",
        "title": "Android Architecture Recommendations",
        "claim": "Separate UI/data layers, repositories, immutable state and lifecycle-aware collection are documented recommendations."
      },
      {
        "url": "https://developer.android.com/topic/architecture/data-layer/offline-first",
        "title": "Android Offline-first Architecture",
        "claim": "Local sources, queues, sync and conflict resolution need explicit policies; offline reads need not imply offline writes."
      },
      {
        "url": "https://developer.android.com/training/data-storage/room/migrating-db-versions",
        "title": "Migrate Your Room Database",
        "claim": "Migration testing should cover all paths; destructive fallback can permanently delete local table data."
      },
      {
        "url": "https://developer.android.com/develop/background-work/background-tasks/persistent",
        "title": "Android WorkManager Task Scheduling",
        "claim": "Work is persisted and rescheduled across reboots with constraints/retries; it is not an exact timer or all immediate work."
      },
      {
        "url": "https://developer.android.com/privacy-and-security/keystore",
        "title": "Android Keystore System",
        "claim": "Keystore protects cryptographic key material and usage constraints; arbitrary credentials need their own protected storage design."
      },
      {
        "url": "https://developer.android.com/build/building-cmdline",
        "title": "Build Android Apps from the Command Line",
        "claim": "Gradle wrapper build/test tasks and signed release artifacts require configured inputs."
      },
      {
        "url": "https://support.google.com/googleplay/android-developer/answer/6346149",
        "title": "Play Console Staged Rollouts",
        "claim": "Halting stops additional staged installs; already-updated users remain on that version."
      }
    ],
    "reviewed_at": "2026-10-11",
    "evidence": "documentation-reviewed"
  },
  "rewrite_url": "/rewrite/guide.md",
  "migration_contracts_url": "/rewrite/contracts.json",
  "agent_workflow": [
    "Read README, dependency manifests, lockfiles, runtime pins and the maintained project workflow before changing anything. Preserve an existing stack unless reconsidering it is part of the request.",
    "Use this guide to select tools by requirement. Resolve compatible versions from official and installed-version documentation; record the actual lockfile and runtime versions.",
    "Create a plan for schema changes, identity, authorization, external effects and deployment. A library choice does not complete its integration.",
    "Run relevant checks after configuration. Exercise the real interface, failure paths and packaged artifact on the intended target. Report commands, versions, results and untested behavior.",
    "On failure, retain the command and diagnostic, fix the failing prerequisite and resume there. Do not regenerate over existing work or suppress failures to make a check green."
  ],
  "selection": {
    "native_ui": true,
    "static_hosting": false,
    "offline_client_candidate": true
  },
  "coverage_note": "Tools are curated starting points for this application role. Exact versions, integrations and offline behavior require project-specific qualification."
}
