← All problems

Mobile Core Concepts

Platform Knowledge

Activity / Fragment / Service lifecycles, Application + ProcessLifecycleOwner, the ART runtime, and the OS-level constraints (process model, Doze, App Standby) every modern Android API assumes you understand.

Free~22 min

Almost every interview question about Android assumes a shared vocabulary: what an Activity is, what survives a configuration change vs a process kill, when the OS will throw your work overboard, and how the bytecode you ship actually runs on the device. This page covers that shared vocabulary - lifecycles, the process model, the ART runtime, and the OS-level background restrictions (Doze, App Standby buckets) that shape every design from the data layer up. Background scheduling itself (WorkManager / FGS / JobScheduler) lives in its own Core Concept; this entry just frames why those constraints exist.

The Android process model

An app on Android is a process, but the lifetime of that process is owned by the system, not by the app. The user "closing" an app by swiping it away does not necessarily end the process; conversely, the OS can kill a backgrounded process at any moment to reclaim RAM. State that has to survive must be explicitly persisted; state that lives only in memory will eventually disappear.

Five process states determine kill priority:

StateTriggered byOS behavior
ForegroundUI visible, or running foreground serviceWon't be killed except in extreme low-memory
VisibleActivity partially visible (over a dialog)Killed only if foreground processes need RAM
ServiceBackground service runningKilled under moderate memory pressure
Cached (visible recently)Backgrounded, kept warm for resumeFirst to be killed
Empty / unusedNo active componentsKilled freely

The OS doesn't tell the app it's about to be killed - it just sends SIGKILL. Anything not persisted is gone. This is why SavedStateHandle exists, why Room over in-memory caches is the default, and why "process death" is a routinely-tested scenario in interview questions.

Activity, Fragment, and Service lifecycles

The three components every Android engineer should be able to draw cold:

Activity

Rendering diagram…

onPause is the contract for "stop interacting with the user" - it must return quickly because the next activity can't start until it does. onStop is "no longer visible" - safer place for heavier teardown. onDestroy is not guaranteed to be called (process kill skips it), so it's not a reliable cleanup hook.

Configuration changes (rotation, locale, dark mode toggle, font scale) destroy and recreate the Activity by default. Anything held in the Activity is rebuilt; ViewModel is the canonical home for state that should survive this.

Fragment

Same shape plus a view lifecycle that runs independently of the Fragment instance lifecycle. The Fragment can outlive its view (onDestroyView without onDestroy) when on the back stack. The footgun: holding view references in Fragment fields leaks the view when it's destroyed but the Fragment isn't.

Use viewLifecycleOwner (not this) when collecting Flows in a Fragment - otherwise the collection outlives the view and crashes on re-creation.

Service

A Service is a component without UI that can run on the main thread (for IPC) or spawn its own threads (rare today - prefer WorkManager). Bound, started, and foreground services have different lifetime rules:

TypeLifetimeUse for
BoundWhile at least one client is boundIPC with another process
StartedUntil stopSelf() or stopService()Mostly obsolete; use WorkManager
ForegroundWhile the notification is visibleUser-perceived ongoing work (music, navigation, upload)

On Android 14+, foreground services must declare a type (dataSync, mediaPlayback, location, phoneCall, etc.). Mismatched types are a runtime crash.

Application + ProcessLifecycleOwner

Application is the singleton that owns the process. onCreate() runs once per process - including after process death and re-creation. Don't put initialization here that depends on user state; that gets re-run on every cold start. Heavy synchronous init here blocks the first frame on every launch, so defer it with the App Startup library or a coroutine that doesn't gate first frame.

ProcessLifecycleOwner exposes a Lifecycle for the whole process, not any single Activity. Its ON_START fires when the app enters foreground (any Activity in any task becomes visible); ON_STOP fires when the last Activity goes away. This is the right hook for "user backgrounded the app" logic - a single Activity's onStop fires too often (it also fires when navigating between screens in the same app).

ProcessLifecycleOwner.get().lifecycle.addObserver(object : DefaultLifecycleObserver {
    override fun onStart(owner: LifecycleOwner) { analytics.appForegrounded() }
    override fun onStop(owner: LifecycleOwner)  { analytics.appBackgrounded() }
})

Sources: Application lifecycle · ProcessLifecycleOwner

ART, AOT, and the bytecode runtime

Your app ships as dex bytecode. ART (Android Runtime, since Android 5; previously Dalvik) is the VM that runs it. The interesting question is how it runs it, and the answer has changed every couple of Android releases.

Rendering diagram…

The current model (Android 9+) layers three sources of compiled output:

LayerSourceWhen applied
Baseline Profile (baseline-prof.txt)You ship it in the APK / AABAt install - immediate
Play cloud profileAggregated from real users by PlayAt install - grows with the user base
Device JIT profileART records hot methods at runtimeApplied during background dexopt (idle + charging)

Profiled methods are AOT-compiled to native; unprofiled methods run interpreted until JIT or the next dexopt window. Compiled output (/data/app/<package>/oat/arm64/base.odex) is device-specific - APK-shipped AOT is impossible because the binary is CPU-arch + OS-version specific.

The practical interview point: shipping a Baseline Profile collapses "first launch is slow until ART learns the app" from a multi-day window to launch 1. App-size and delivery covers this in more depth; what to know here is that it exists and what problem it solves.

Sources: Baseline Profiles · ART overview

Doze, App Standby, and background limits

The OS aggressively restricts what backgrounded apps can do. Three overlapping systems, all interview-relevant:

Doze (Android 6+) - when the device is idle (stationary, screen off, unplugged), the OS defers network access, wakelocks, alarms, and jobs. Brief "maintenance windows" let queued work run periodically. The deeper the doze, the longer between maintenance windows.

App Standby buckets (Android 9+) - per-app, based on recent usage. An app the user opens daily lives in Working Set; one opened monthly lands in Rare. Bucket determines how often a backgrounded app's jobs can run. The four standard buckets (Active, Working Set, Frequent, Rare) arrived with App Standby in Android 9 (API 28); the Restricted bucket was added later, in Android 11 (API 30).

BucketNetwork / job frequencyWhen
ActiveUnrestrictedApp in foreground recently
Working SetFrequentDaily use
FrequentLess frequentWeekly use
RareMinimalMonthly use
Restricted (API 30+)Heavy restrictionFlagged or never-used

Foreground service exemption - a foreground service (with its required visible notification) is exempt from Doze for its duration. This is why long uploads and ongoing media playback wrap themselves in an FGS.

You should almost never implement Doze handling yourself. WorkManager picks the right scheduling layer (JobScheduler / AlarmManager / FGS) and respects the OS constraints automatically. The Background work + scheduling Core Concept covers WorkManager's contract; this entry just clarifies why those constraints exist.

Sources: Power management restrictions

See also

  • Concurrency - lifecycle-scoped coroutines (viewModelScope, lifecycleScope, ProcessLifecycleOwner's scope) are the canonical glue between this entry and async work.
  • Background Work and Scheduling - WorkManager is what you actually call to schedule work that respects Doze and App Standby.
  • State Management - SavedStateHandle is the only ViewModel-adjacent thing that survives process kill.
  • App Size and Delivery - Baseline Profiles and their delivery via AAB.

Used in

  • Crash Reporter SDK - the JVM uncaught-exception handler runs synchronously on a dying process; the breadcrumb buffer and report writer must be safe under abrupt termination. The next-launch upload depends on Application.onCreate running before the worker fires.
  • Photo Gallery App - UploadWorker with setForeground() is the FGS-exemption pattern in practice; the upload survives Doze + swipe-away because the OS treats it as user-visible work.
  • Messenger App - WebSocket reconnection on app foregrounding hooks ProcessLifecycleOwner.ON_START, not Activity-level callbacks.
  • Ride-Sharing App - foreground service of type location is the only legal way to keep GPS active in background since Android 10.
  • Newsfeed App - ContentObserver registration is Application-scoped so it survives backgrounding; per the Photo Gallery cross-link, that's the same pattern as device-photo detection.

Done reading? Mark it so it sticks in your dashboard.