Android Kotlin
Beginner-to-advanced Android application reference covering Kotlin essentials, platform components, lifecycle, state, resources, intents, permissions, storage, and scalable app architecture.
Kotlin Essentials for Android
Use Kotlin's type system to make Android boundaries explicit and safe.
Null Safety & Platform Boundaries
Model absence with nullable types, narrow once, and avoid assertions at lifecycle and Java interop boundaries.
?: return finish()Fail or exit at the boundary so the rest of the function uses a non-null value.
?.letRun an operation only when the receiver is non-null.
Lifecycle timing, missing Bundle keys, Java platform types, and process recreation make non-null assertions fragile. Validate at boundaries or model a loading/absent state.
data, sealed, enum & value Types
Use data classes for immutable values, sealed hierarchies for closed alternatives, enums for fixed constants, and value classes to prevent primitive confusion.
A sealed state prevents contradictory flags such as loading=true and error!=null. A data class is better when states can coexist independently.
Extensions, Delegation & Scope Functions
Use extensions for discoverable boundary helpers, delegation for reusable behavior, and scope functions only when the receiver/result distinction stays obvious.
An extension does not modify or virtually override the receiver type. Prefer an injected collaborator when behavior needs state, substitution, or polymorphism.
Android Components & Process Model
Know which platform component owns entry, UI, background, and cross-app work.
Activity, Service, Receiver & Provider
Activities host user-facing windows; services perform user-visible ongoing work; receivers handle brief broadcasts; providers expose structured data across process boundaries.
Android may kill a background process without calling a final callback. Persist durable state as it changes; never rely on an Application singleton as permanent storage.
Context & Memory Ownership
Activity context carries UI configuration and window identity; application context follows the process and must not own screen objects.
Long-lived objects retaining Activity, Fragment, View, binding, or callback references leak an obsolete UI after configuration changes.
Lifecycle, Configuration & State Restoration
Match every value and task to the lifetime it must survive.
Lifecycle Ownership
Lifecycle callbacks describe transitions, not guaranteed paired events. Use lifecycle-aware APIs and keep callbacks small.
Process death may bypass cleanup callbacks. Persist important user data immediately or transactionally; use callbacks only to release resources while the process remains alive.
ViewModel, Saved State & Persistent Storage
Choose storage by lifetime, size, and source of truth rather than convenience.
| State Holder | Recomposition | Configuration Change | Process Death | Durable Relaunch | Best For |
|---|---|---|---|---|---|
| remember | Yes | No | No | No | Ephemeral composable-local state |
| rememberSaveable | Yes | Yes | Usually, if saveable | No | Small UI state such as input and selection |
| ViewModel | Yes | Yes | No by itself | No | Screen state and orchestration |
| SavedStateHandle | Yes | Yes | Yes for small saveable values | No | Navigation arguments and restoration keys |
| Room / DataStore / file | Yes when observed | Yes | Yes | Yes | Authoritative durable application or preference data |
A ViewModel survives owner recreation such as rotation, not system process termination. SavedStateHandle is for small reconstruction inputs, not a database replacement.
SavedStateHandle
Store small, serializable inputs needed to reconstruct screen state after process recreation.
Save the query or entity ID, then reload canonical records. Saving whole object graphs risks transaction-size failures and stale duplicated data.
Resources, Localization & Configuration
Keep user-facing values in resources and design for runtime configuration changes.
Resource Types & Qualifiers
Use strings, plurals, dimensions, colors, drawables, and qualified alternatives so Android selects the best resource for locale, density, size, and night mode.
Word order and plural rules vary by language. Use formatted string/plural resources with translator context rather than assembling user-visible fragments in Kotlin.
Configuration & Adaptive UI
Rotation, resize, locale, font scale, density, theme, and device posture can change while the app runs.
Regularly enable developer options that destroy activities and test rotation/resizing. Bugs found there often reveal misplaced ownership or missing persistence.
Data, Storage & Background Work
Select persistence and scheduling tools based on data shape, consistency, durability, and user visibility.
Room, DataStore, Files & Cache
Room fits relational/queryable data; DataStore fits small settings; files fit opaque or streamed data; cache is disposable and must have a source of truth.
Test schema and preference migrations against real historical versions. Destructive fallback can erase user data and should be an explicit product decision.
Coroutine, WorkManager, Alarm or Foreground Service
Use a coroutine for in-process lifetime-bound work, WorkManager for persistent deferrable jobs, alarms for precise user-facing time semantics when allowed, and foreground services for ongoing noticeable work.
Android background execution and permission rules evolve. Check official guidance for your target SDK before shipping scheduling, alarms, or foreground services.
Architecture, Security & Production Readiness
Scale through clear ownership and boundaries, not layers added by habit.
UI, Data & Optional Domain Layers
The UI renders state and sends actions; repositories expose application data; use cases are valuable when business logic is reused or a ViewModel becomes complex.
Choose one authoritative owner for each piece of data. Other layers observe or derive it rather than maintaining unsynchronized copies.
Dependency Injection & Boundaries
Prefer constructor injection. Use Hilt when component graph complexity warrants it; manual composition is sufficient for small apps.
Passing Context, a container, or a global singleton deep into business code hides dependencies. Inject the narrow API the class needs.
Production Checklist
Audit boundary validation, exported components, lifecycle ownership, state restoration, accessibility, performance, and observability.
Use the dedicated Coroutines, Flows, Compose, Modifiers, Testing, Collections, and Naming sheets for mechanics intentionally excluded here.