3
The Activity Lifecycle

What good is an app that resets itself when the user rotates the device? At the end of Chapter 2 you discovered that the geography question displayed is reset to the first question every time the device is rotated, regardless of what question is displayed prior to rotation. In this chapter you will address the dreaded – and very common – rotation problem. To fix it, you will learn the basics of the activity lifecycle.

Every instance of Activity has a lifecycle. During this lifecycle, an activity transitions between four states: resumed, paused, stopped, and nonexistent. For each transition, there is an Activity method that notifies the activity of the change in its state. Figure 3.1 shows the activity lifecycle, states, and methods.

Figure 3.1  Activity state diagram

Figure shows Activity State diagram.  Activities are represented by process symbols and are connected to each other by bidirectional flowlines.

Figure 3.1 indicates for each state whether the activity has an instance in memory, is visible to the user, or is active in the foreground (accepting user input). Table 3.1 summarizes this information.

Table 3.1  Activity States

State In memory? Visible to user? In foreground?

nonexistent

no

no

no

stopped

yes

no

no

paused

yes

yes/partially*

no

resumed

yes

yes

yes

(*Depending on the circumstances, a paused activity may be fully or partially visible. This is discussed further in the section called Exploring the activity lifecycle by example.)

The resumed state represents the activity the user is currently interacting with. Only one activity across all the apps on the device can be in the resumed state at any given time.

Subclasses of Activity can take advantage of the methods named in Figure 3.1 to get work done at critical transitions in the activity’s lifecycle. These methods are often called lifecycle callbacks.

You are already acquainted with one of these lifecycle callback methods – onCreate(Bundle). The OS calls this method after the activity instance is created but before it is put on screen.

Typically, an activity overrides onCreate(Bundle) to prepare the specifics of its UI:

  • inflating widgets and putting them on screen (in the call to (setContentView(int))

  • getting references to inflated widgets

  • setting listeners on widgets to handle user interaction

  • connecting to external model data

It is important to understand that you never call onCreate(Bundle) or any of the other Activity lifecycle methods yourself. You simply override the callbacks in your activity subclass. Then Android calls the lifecycle callbacks at the appropriate time (in relation to what the user is doing and what is happening across the rest of the system) to notify the activity that its state is changing.

Logging the Activity Lifecycle

In this section, you are going to override lifecycle methods to eavesdrop on QuizActivity’s lifecycle. Each implementation will simply log a message informing you that the method has been called. This will help you see how QuizActivity’s state changes at runtime in relation to what the user is doing.

Making log messages

In Android, the android.util.Log class sends log messages to a shared system-level log. Log has several methods for logging messages. Here is the one that you will use most often in this book:

    public static int d(String tag, String msg)

The d stands for debug and refers to the level of the log message. (There is more about the Log levels in the final section of this chapter.) The first parameter identifies the source of the message, and the second is the contents of the message.

The first string is typically a TAG constant with the class name as its value. This makes it easy to determine the source of a particular message.

Open QuizActivity.java and add a TAG constant to QuizActivity:

Listing 3.1  Adding a TAG constant (QuizActivity.java)

public class QuizActivity extends AppCompatActivity {

    private static final String TAG = "QuizActivity";
    ...
}

Next, in onCreate(Bundle), call Log.d(…) to log a message.

Listing 3.2  Adding a log statement to onCreate(Bundle) (QuizActivity.java)

public class QuizActivity extends AppCompatActivity {
    ...
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(TAG, "onCreate(Bundle) called");
        setContentView(R.layout.activity_quiz);
        ...
    }
}

Now override five more methods in QuizActivity by adding the following after onCreate(Bundle):

Listing 3.3  Overriding more lifecycle methods (QuizActivity.java)

public class QuizActivity extends AppCompatActivity {
    ...
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...
    }

    @Override
    public void onStart() {
        super.onStart();
        Log.d(TAG, "onStart() called");
    }

    @Override
    public void onResume() {
        super.onResume();
        Log.d(TAG, "onResume() called");
    }

    @Override
    public void onPause() {
        super.onPause();
        Log.d(TAG, "onPause() called");
    }

    @Override
    public void onStop() {
        super.onStop();
        Log.d(TAG, "onStop() called");
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy() called");
    }
    ...
}

Notice that you call the superclass implementations before you log your messages. These superclass calls are required. Calling the superclass implementation should be the first line of each callback method override implementation.

You may have been wondering about the @Override annotation. This asks the compiler to ensure that the class actually has the method that you want to override. For example, the compiler would be able to alert you to the following misspelled method name:

public class QuizActivity extends AppCompatActivity {

    @Override
    public void onCreat(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ...
    }
    ...
}

The parent AppCompatActivity class does not have an onCreat(Bundle) method, so the compiler will complain. This way you can fix the typo now rather than waiting until you run the app and see strange behavior to discover the error.

Using Logcat

To access the log while the application is running, you can use Logcat, a log viewer included in the Android SDK tools.

When you run GeoQuiz, you should see Logcat appear at the bottom of Android Studio, as shown in Figure 3.2. If Logcat is not visible, select the Android Monitor tool window near the bottom of the screen and ensure that the logcat tab is selected.

Figure 3.2  Android Studio with Logcat

Screenshot shows Android Studio with LogCat.

Run GeoQuiz and messages will start materializing in Logcat. By default, log statements that are generated with your app’s package name are shown. You will see your own messages along with some system output.

To make your messages easier to find, you can filter the output using the TAG constant. In Logcat, click the dropdown in the top right of the Logcat pane that reads Show only selected application. This is the filter dropdown, which is currently set to show messages from only your app. Selecting No Filters will show log messages generated from all over the system.

In the filter dropdown, select Edit Filter Configuration to create a new filter. Name the filter QuizActivity and enter QuizActivity in the Log Tag field (Figure 3.3).

Figure 3.3  Creating a filter in Logcat

Screenshot shows Create New Logical filter window.

Click OK. Now, only messages tagged QuizActivity will be visible in Logcat (Figure 3.4).

Figure 3.4  Launching GeoQuiz creates, starts, and resumes an activity

Screenshot shows the activity log for QuizActivity.

Exploring the activity lifecycle by example

Three lifecycle methods were called after GeoQuiz was launched and the initial instance of QuizActivity was created: onCreate(Bundle), onStart(), and onResume() (Figure 3.4). Your QuizActivity instance is now in the resumed state (in memory, visible, and active in the foreground).

(If you are not seeing the filtered list, select the QuizActivity filter from Logcat’s filter dropdown.)

Now let’s have some fun. Press the Back button on the device and then check Logcat. Your activity received calls to onPause(), onStop(), and onDestroy() (Figure 3.5). Your QuizActivity instance is now in the nonexistent state (not in memory and thus not visible – and certainly not active in the foreground).

Figure 3.5  Pressing the Back button destroys the activity

Screenshot shows the activity log for QuizActivity.

When you pressed the Back button, you told Android, I’m done with this activity, and I won’t need it anymore. Android then destroyed your activity’s view and removed all traces of the activity from memory. This is Android’s way of being frugal with your device’s limited resources.

Launch GeoQuiz again by clicking the GeoQuiz app icon. Android creates a new instance of QuizActivity from scratch and calls onCreate(), onStart(), and onResume() to move QuizActivity from nonexistent to resumed.

Now press the Home button. The home screen displays and QuizActivity moves completely out of view. What state is QuizActivity in now? Check Logcat for a hint. Your activity received calls to onPause() and onStop(), but not onDestroy() (Figure 3.6).

Figure 3.6  Pressing the Home button stops the activity

Screenshot shows the activity log for QuizActivity.

Pressing the Home button means the user is telling Android, I’m going to go look at something else, but I might come back. I’m not really done with this screen yet. Android pauses and ultimately stops your activity. This means, after pressing Home, your instance of QuizActivity hangs out in the stopped state (in memory, not visible, and not active in the foreground). Android does this so it can quickly and easily restart QuizActivity where you left off when you come back to GeoQuiz later.

(This is not the whole story about going Home. Stopped activities can be destroyed at the discretion of the OS. See the section called The Activity Lifecycle, Revisited for the rest of the story.)

Go back to GeoQuiz by selecting the GeoQuiz task card from the overview screen. To do this, press the Recents button next to the Home button (Figure 3.7). (On devices without a Recents button, long-press the Home button.)

Figure 3.7  Back, Home, and Recents buttons

Screenshot shows buttons in the Android phone. The Back, Home, and Recent buttons are labeled.

Each card in the overview screen represents an app the user has interacted with in the past (Figure 3.8). (The overview screen is often called the “Recents screen” or “task manager” by users. We defer to the developer documentation, which calls it the “overview screen.”)

Figure 3.8  Overview screen

Screenshot shows Overview screen in an Android phone. The Settings, BeatBox, CriminalIntent, and GeoQuiz Applications are shown running.

Click on the GeoQuiz task card in the overview screen. QuizActivity will fill the screen.

A quick look at Logcat shows that your activity got calls to onStart() and onResume(). Note that onCreate() was not called. This is because QuizActivity was in the stopped state after the user pressed the Home button. Because the activity instance was still in memory, it did not need to be created. Instead, the activity only had to be started (moved to the paused/visible state) and then resumed (moved to the resumed/foreground state).

It is also possible for an activity to hang out in the paused state (fully or partially visible, but not in the foreground). The partially visible paused scenario can occur when a new activity with either a transparent background or a smaller-than-screen size is launched on top of your activity. The fully visible scenario occurs in multi-window mode (only available on Android 6.0 Nougat and higher) when the user interacts with a window that does not contain your activity, and yet your activity remains fully visible in the other window.

As you continue through the book, you will override the different activity lifecycle methods to do real things for your application. When you do, you will learn more about the uses of each method.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.16.76.43