Interview QuestionPractical QuestionFollow-up Questions

Activity Lifecycle and State Preservation

skydovesJaewoong Eum (skydoves)||14 min read

Activity Lifecycle and State Preservation

Every Android Activity follows a strictly defined lifecycle managed by the ActivityManager service and the ActivityThread class in the framework. When the system brings an Activity to the foreground, pauses it for a phone call, or destroys it to reclaim memory, each transition triggers a specific callback that gives the developer a chance to acquire resources, save state, or release references. Misunderstanding the order or guarantees of these callbacks is one of the most common sources of data loss and visual glitches in production apps. By the end of this lesson, you will be able to:

  • Trace the exact sequence of lifecycle callbacks when an Activity is interrupted by a phone call and later resumed.
  • Explain the role of ActivityThread and Instrumentation in dispatching lifecycle events internally.
  • Distinguish between transient UI state and persistent data, and identify the correct callback for saving each.
  • Describe how onSaveInstanceState interacts with the Bundle mechanism and the SavedStateRegistry in modern Jetpack components.
  • Apply lifecycle awareness to avoid common pitfalls like committing fragment transactions after onSaveInstanceState.

How the Framework Dispatches Lifecycle Callbacks

The lifecycle of an Activity is not managed by the Activity itself. The system server process (system_server) hosts the ActivityManagerService, which tracks every Activity across all processes. When a state transition is needed, ActivityManagerService sends a transaction to the app process through Binder IPC. On the app side, ActivityThread (the main thread's entry point) receives these transactions and dispatches them.

Internally, lifecycle transitions are modeled as ClientTransactionItem objects. For example, when the system decides to pause an Activity, it sends a PauseActivityItem transaction. ActivityThread processes this transaction and calls into Instrumentation.callActivityOnPause(), which in turn calls Activity.performPause(). This method updates internal state flags and then invokes the developer-facing onPause() callback:

// Simplified view of the framework dispatch chain
// ActivityThread.java (framework source)
private fun handlePauseActivity(token: IBinder, finishing: Boolean) {
    val r = mActivities[token]  // ActivityClientRecord
    performPauseActivity(r, finishing)
}

private fun performPauseActivity(r: ActivityClientRecord, finishing: Boolean) {
    mInstrumentation.callActivityOnPause(r.activity)
    r.paused = true
}

The key insight is that the framework controls the ordering absolutely. Developers cannot reorder callbacks or skip them. The framework also decides whether onSaveInstanceState is called based on whether the Activity might be destroyed without the user explicitly closing it.

Lifecycle Transitions During a Phone Call Interruption

When a user is actively using an app and an incoming phone call arrives, the phone app's Activity is brought to the foreground, completely obscuring the current Activity. The system triggers a precise sequence of transitions:

Leaving the foreground:

  1. onPause() -- The Activity loses foreground focus. At this point the window is still attached but the Activity is no longer the topmost interactive surface. The framework guarantees this callback completes before the next Activity's onResume() runs, so it must execute quickly.

  2. onStop() -- The Activity is no longer visible at all. The phone call UI fully covers it. At this point the Activity's window surface may be released by the window manager to save GPU memory.

  3. onSaveInstanceState(outState: Bundle) -- Because the Activity is being stopped in a way that might lead to process death (the user did not explicitly finish it), the framework calls this callback. For applications targeting API 28 (Android P) and above, onSaveInstanceState is guaranteed to be called after onStop(). The default implementation walks the view hierarchy and saves the state of every View that has an ID (EditText content, scroll positions, checked states).

The Activity object remains in memory in the stopped state for the duration of the phone call, assuming the system does not need to kill the process for memory.

Returning to the foreground:

  1. onRestart() -- Called exclusively when an Activity transitions from the stopped state back to the started state. This callback does not fire during initial creation, making it useful for logic that should only run on return visits, such as refreshing stale data.

  2. onStart() -- The Activity becomes visible again. The window surface is reattached. Registered LifecycleObserver instances receive the ON_START event.

  3. onResume() -- The Activity reaches the top of the activity stack and becomes interactive. Input events are delivered to this Activity again.

Activity Lifecycle During Phone Call

This interview continues for subscribers

Subscribe to Dove Letter for full access to exclusive interviews about Android and Kotlin development.

Become a Sponsor