Android Lifecycle

Understanding the Android lifecycle and responding correctly to state changes is crucial to building apps with fewer bugs that use fewer resources and provide a good user experience. By Denis Buketa.

Leave a rating/review
Download materials
Save for later
Share

When it comes to building an Android app, activities and fragments are key components to building its user interface (UI). As the user navigates within the app, these components go through different states of the Android lifecycle.

Understanding the lifecycle and responding correctly to its state changes is crucial. It allows you to build apps that have fewer bugs, use fewer Android system resources and provide a good user experience.

In this tutorial, you’ll play with a simple app that you can use to count dogs as you walk around the neighborhood: PuppyCounter. You’ll learn:

  • The role of the Android lifecycle in apps.
  • The basics of the activity and fragment lifecycles, and the callbacks invoked when the activity moves between lifecycle states.
  • How to save and restore the activity instance state.
  • The process for passing the data between activities and fragments.
  • How ViewModel can help you store and manage UI-related data in a lifecycle-conscious way.

For this tutorial, you need Android Studio and an Android device or emulator.

Getting Started

To start, download the materials by using the Download Materials button at the top or bottom of this tutorial. Open the starter project in Android Studio. Once the project opens, let it build and sync, and you’ll be ready to go!

Run the app and check its features:

The main and share screens in the PuppyCounter app

The app has two screens:

  • Main screen: Allows you to count dogs of different sizes. You can tap the cards or the plus and minus buttons to update the counters. In the top bar, you have two buttons: The first one resets the values and the second one opens the Share screen.
  • Share screen: Displays your dog count. Tap Share to open a dialog that asks you if you want to share your count. In this tutorial, you won’t implement that logic, but you can pretend that it exists. :]

Next, check out the project structure:

Puppy Counter app's project structure

As you can see, a lot is already prepared for you. The most important packages are activities and fragments. You’ll move between them as you learn about activity and fragment lifecycle. Right now, don’t worry about the details. You’ll become familiar with classes inside as you go through the tutorial. Besides those packages, notice these three things:

  • DogCount: a model class for your data.
  • SplashActivity: a splash activity that opens up when you start the app.
  • PuppyCounterApplication: the app class.

Before going into details of the activity lifecycle, here’s some background on the lifecycle’s role in Android apps.

Understanding the Role of the Lifecycle in Apps

The Android operating system (OS) is a multi-user Linux system. In most cases, every app runs in its own Linux process. The OS creates the process when any of the app components need to execute. When no app component is running and the OS needs to free memory to run other apps, it kills the process.

The Android OS uses an importance hierarchy to determine which processes to keep alive or to kill. The importance hierarchy categorizes processes as different types. That type depends on the app components that are currently running and their current state.

The most common app component is Activity. Every Android app has one or more activities. As the user navigates within the app, activities go through different lifecycle states.

Developers must understand how different components impact the process’s lifetime. Not using these components correctly can result in the system killing the process while it’s doing important work.

Exploring the Activity Lifecycle

Activity lifecycle and lifecycle callbacks

The figure above shows different states that activity goes through during its lifecycle:

  • Initialized: Activity instance is created and its properties initialized.
  • Created: Activity is now completely initialized and ready to configure its UI.
  • Started: Activity is visible to the user.
  • Resumed: Activity is visible to the user and has focus. In this state, the user is likely interacting with the activity.
  • Destroyed: Activity is destroyed and the OS can reclaim its memory.

Notice different callbacks between states. The OS invokes these callbacks when the activity moves from one state to another. You can override those methods in your own activities to perform tasks in response to those lifecycle state changes.

Before explaining each callback, check them out in action. Open MainActivity.kt and add logging to onCreate() and onResume(). Override other callbacks as well:

override fun onStart() {
  Timber.i("PuppyCounter - MainActivity - onStart()")
  super.onStart()
}

override fun onCreate(savedInstanceState: Bundle?) {
  Timber.i("PuppyCounter - MainActivity - onCreate()")
  super.onCreate(savedInstanceState)
  setContentView(R.layout.layout_main)

  findViews()

  setupSmallDogViewsClickListeners()
  setupMiddleDogViewsClickListeners()
  setupBigDogViewsClickListeners()
}

override fun onResume() {
  Timber.i("PuppyCounter - MainActivity - onResume()")
  super.onResume()
  renderDogCount(dogCount)
}

override fun onPause() {
  Timber.i("PuppyCounter - MainActivity - onPause()")
  super.onPause()
}

override fun onStop() {
  Timber.i("PuppyCounter - MainActivity - onStop()")
  super.onStop()
}

override fun onDestroy() {
  Timber.i("PuppyCounter - MainActivity - onDestroy()")
  super.onDestroy()
}

Whenever you override a callback like this, make sure that you call the super class method as well. If you don’t do that, your app won’t execute some important work and it might crash or end up in a weird state.

Build and run the app. Then, check the logs. To inspect the logs in Android Studio, open the Logcat tool by clicking Logcat at the bottom. Enter PuppyCounter in Search History to show the results:

Arrow pointing to Logcat tool in Android Studio

Next, close the app by tapping back, or swipe back if you have gestural navigation enabled. Check the logs again. You should see something like this:

Logs showing full activity lifecycle

Note: You won’t see “Back button clicked” in your logs. That’s added to make it easier for you to spot the button taps.

Understanding Activity Lifecycle Callbacks

You just went through one complete activity lifecycle: Activity was created, resumed and finally destroyed when you exited the app.

Diagram of activity lifecycle callbacks

The diagram above represents an activity lifecycle:

There are situations where the system will kill a process hence not calling onDestroy() or any other activity lifecycle methods. So, it should not be used to do things that are intended to remain around after the process goes away.

  • onCreate(): Activity enters the Created state. Here, you perform logic that should happen only once for the entire life of the activity. This can include setting the content view, associating the activity with a ViewModel, instantiating some class-scope variables, etc.
  • onStart(): Activity enters the Started state. This call makes the activity visible to the user, as the app prepares for the activity to enter the foreground and become interactive.
  • onResume(): Activity enters the Resumed state. The user can now interact with the activity. Here, you can enable any functionality that needs to run while the component is visible and in the foreground.
  • onPause(): Activity enters the Paused state. This call indicates that the activity is no longer in the foreground, though it may still be visible if the user is in multi-window mode, for example. During this time, you should pause or adjust operations that shouldn’t continue or should continue in moderation. The activity remains in this state until either the activity resumes — for example, opening or closing the bottom sheet in the activity — or until it becomes completely invisible to the user — such as when opening another activity.
  • onStop(): Activity enters the Stopped state. Activity is no longer visible to the user. Here, you should release or adjust resources that aren’t needed while the activity isn’t visible to the user. You should also use this opportunity to perform shutdown operations on tasks that are relatively CPU-intensive, like operations on the database, for example.
  • onDestroy(): Activity enters the Destroyed state. Here, the activity is finishing. This can be because:
    • The user is completely dismissing the activity.
    • finish() is being called on the activity.
    • The system is temporarily destroying the activity due to a configuration change, such as device rotation or multi-window mode.

    There are situations where the system will kill a process hence not calling onDestroy() or any other activity lifecycle methods. So, it should not be used to do things that are intended to remain around after the process goes away.

Note: For more information about the Activity lifecycle, refer to the Android Developer documentation.