Kahibaro
Discord Login Register

7.1 Activity Lifecycle

Why Activity Lifecycle Matters

Every screen in a classic Android app is represented by an Activity. An activity does not simply start and stop. It moves through a sequence of states while the user opens your app, navigates around, receives calls, switches apps, rotates the device, and eventually leaves.

Understanding this sequence is essential because your code runs inside specific lifecycle callbacks. If you know when these callbacks happen, you can decide where to start tasks, where to stop them, and how to release resources correctly.

This chapter focuses on the specific stages of an activity’s lifecycle and on the order in which the main callback methods are called.

The Core Activity States

At a high level, an activity moves through three broad human friendly states. It can be created and visible, it can be partially visible or paused, or it can be completely stopped and eventually destroyed.

Android represents these states with a well defined set of methods. You never call these lifecycle methods yourself. The system calls them on your activity instance at the right time. Your responsibility is to override the ones you need and put the right logic in them.

The most commonly discussed states are:

An activity is created and initialized, then started and becomes visible, then resumed and becomes interactive. Later it can be paused when another screen partially covers it, stopped when it is no longer visible, and finally destroyed when the system removes it from memory or you finish it.

The Main Lifecycle Callbacks

The Android framework provides a number of lifecycle methods. The most important ones for every activity are onCreate, onStart, onResume, onPause, onStop, and onDestroy.

You typically override these methods in your Activity subclass and add logging or logic so you know what is happening. A minimal example looks like this:

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // Initialize layout and core components here
        setContentView(R.layout.activity_main)
    }
    override fun onStart() {
        super.onStart()
        // Activity is becoming visible
    }
    override fun onResume() {
        super.onResume()
        // Activity is now in the foreground and interactive
    }
    override fun onPause() {
        super.onPause()
        // Activity is partially obscured, commit ongoing changes
    }
    override fun onStop() {
        super.onStop()
        // Activity is no longer visible, release heavier resources
    }
    override fun onDestroy() {
        super.onDestroy()
        // Final cleanup before the activity is removed
    }
}

Each of these methods corresponds to a step in the lifecycle and is always called in a specific order that you can rely on.

From Launch to Interactive

When the user launches your activity for the first time, or when the system needs to show it again after it was stopped, the framework moves it through a predictable sequence of callbacks.

First, onCreate is called. This is where you usually call setContentView, set up views, initialize state, and restore any previously saved data if needed. At this moment the activity object exists but it is not yet visible to the user.

Next, onStart is called. The activity is now becoming visible on the screen, but the user still cannot interact with it. The view hierarchy has been created and attached. If you need to start logic that is tied to visibility, this is the earliest point where the activity is known to be visible.

Then, onResume is called. Now the activity is in the foreground and the user can interact with it. Touch events, keyboard input, and other interactions are delivered in this state. This is the normal running state for an activity while the user is actively using it.

A typical first run sequence is:

onCreateonStartonResume

Later, if the activity comes back from the background without being destroyed, your code will generally see:

onRestartonStartonResume

The onRestart method is invoked only when a stopped activity is started again, and gives you a chance to handle this transition specifically if you need to.

When the Activity Loses Focus

When something happens that takes focus away from your activity, the system calls pause related methods. This can happen for several reasons such as the user pressing the Home button, an incoming call, a dialog on top, or another full screen activity starting.

The first method in this direction is onPause. At this point your activity is still partially alive, but it is no longer the one that receives user input. It might still be visible, for example if a dialog covers only part of it.

onPause is where you commit any unpersisted changes that must not be lost. It is also where you should stop operations that must not continue if the user is not actively using the screen, such as ongoing UI animations or sensitive tasks.

After onPause, one of two things can happen. If the activity is no longer visible at all, onStop is called. If the user quickly returns, the system can sometimes skip to onResume again without ever calling onStop, for example when a transient dialog goes away.

onStop tells you that the activity is completely hidden. It is still retained in memory but is no longer on the screen. This is a suitable place to release resources that do not need to be kept while the activity is not visible.

The usual path when the user leaves your activity for another one is:

onPauseonStop

If the user then comes back to your activity and the system kept it in memory, you see:

onRestartonStartonResume

Activity Destruction

At some point, your activity will be destroyed. This may happen because your code called finish(), the user used the Back button, or the system needs to free memory and decides to remove your activity process.

onDestroy is the final callback the activity receives before it is fully discarded. It can be called after onStop because the activity is finishing or because the system is temporarily destroying the activity during events covered separately in configuration changes.

In a normal finish, such as when the user presses the Back button, the sequence from interactive state to destruction is:

onPauseonStoponDestroy

The system does not guarantee that onDestroy will always be called. For example, if the process hosting your activity is killed without a proper shutdown, your code will not see it. This is why any essential state saving must not rely only on onDestroy.

Typical Lifecycle Sequences

Since the callbacks always occur in order, you can think of normal usage patterns as small sequences.

When the activity is first created:

onCreateonStartonResume

When a new activity comes in front and fully covers the current one:

onPauseonStop

When the user returns to a stopped but still existing activity:

onRestartonStartonResume

When the activity is finished from the interactive state:

onPauseonStoponDestroy

These ordered sequences help you reason about when to allocate and release resources.

The lifecycle callbacks are always called in a strict forward order. You will never see onResume called before onStart, and you will never see onStop called before onPause.

Using Logs to Observe Lifecycle Events

For a beginner, the lifecycle can feel abstract until you see it in action. A simple way to observe it is to add log statements in each lifecycle method of your Activity.

You can use the Android logging API and the Logcat window in Android Studio. For example:

class MainActivity : AppCompatActivity() {
    private val tag = "MainActivityLifecycle"
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        Log.d(tag, "onCreate")
        setContentView(R.layout.activity_main)
    }
    override fun onStart() {
        super.onStart()
        Log.d(tag, "onStart")
    }
    override fun onResume() {
        super.onResume()
        Log.d(tag, "onResume")
    }
    override fun onPause() {
        super.onPause()
        Log.d(tag, "onPause")
    }
    override fun onStop() {
        super.onStop()
        Log.d(tag, "onStop")
    }
    override fun onDestroy() {
        super.onDestroy()
        Log.d(tag, "onDestroy")
    }
    override fun onRestart() {
        super.onRestart()
        Log.d(tag, "onRestart")
    }
}

Once you run the app, open Logcat and filter by the tag you defined. Then, perform actions like rotating the device, pressing Home, pressing Back, and launching other activities. You will see the sequence of lifecycle methods that the system invokes in response.

High Level Mapping to User Actions

Even though the lifecycle is controlled by the system, it is often convenient to roughly map it to typical user actions.

When the user opens your app from the launcher, the operating system creates a new activity instance for the entry screen. You see the creation methods, then the app becomes interactive.

When the user taps Home, that activity moves into the background but remains stopped, and its data is kept in memory as long as resources permit.

When the user returns from the Overview or Recent Apps screen, the previously stopped activity usually goes through restart and then becomes interactive again.

When the user presses Back to leave the last activity of your app, that activity is finished and removed from the history stack, which leads to its destruction callbacks.

Internally the system might also destroy and later recreate your activity for reasons not directly tied to the user action, which will be explored further in the separate chapter on configuration changes. Here the important detail is that you use the lifecycle callbacks to react whenever the system changes the state of your activity, no matter what triggered the change.

Views: 3

Comments

Please login to add a comment.

Don't have an account? Register now!