Understanding the Activity Lifecycle
As you have seen, an activity is simply a screen or user interface in an Android application—either a full screen or a floating window that a user interacts with. An Android app is made up of different activities that interact with the user as well as one another. For example, a simple calculator would use one single activity. If you enhanced the calculator app to switch between a simple version and a scientific version, you would then use two activities.
Every Android application runs inside its own process. Processes are started and stopped to run an application and also can be killed to conserve memory and resources. Activities, in turn, are run inside the main UI thread of the application’s process.
Once an activity is launched, it goes through a lifecycle, a term that refers to the steps the activity progresses through as the user (and OS) interacts with it. There are specific method callbacks that let you react to the changes during the activity lifecycle.
The activity lifecyle has four states.
- When the activity is on the foreground of the application, it is the running activity. Only one activity can be in the running state at a given time.
- If the activity loses focus but remains visible (because a smaller activity appears on top), the activity is paused.
- If the activity is completely covered by another running activity, the original activity is stopped. When an activity stops, you will lose any state and will need to re-create the current state of the user interface when the activity is restarted.
- While the activity is paused or stopped, the system can kill it if it needs to reclaim memory. The user can restart the activity.
While the application moves through the different states, the android.app.Activity lifecycle methods (or callbacks) get called by the system. These callbacks are as follows.
- onCreate(Bundle savedInstanceState)is called when the activity is created for the first time. You should initialize data, create an initial view, or reclaim the activity’s frozen state if previously saved (this is covered later). The onCreate callback is always followed by onStart.
- onStart() is called when the activity is becoming visible. This is an ideal place to write code that affects the UI of the application, such as an event that deals with user interaction. This callback is normally followed by onResume but could be followed by onStop if the activity becomes hidden.
- onResume() is called when the activity is running in the foreground and the user can interact with it. It is followed by onPause.
- onPause() is called when another activity comes to the foreground. The implementation needs to be quick, because the other activity cannot run until this method returns. The onPause callback is followed by onResume if the activity returns to the foreground, or by onStop if the activity becomes invisible.
- onStop() is called when the activity is invisible to the user; either a new activity has started, an existing activity has resumed, or this activity is getting destroyed. The onStop callback is followed by onRestart if the activity returns to the foreground.
- onRestart() is called when the activity is being restarted, as when the activity is returning to the foreground. It is always followed by onStart.
- onDestroy() is called by the system before the activity is destroyed, either because the activity is finishing or because the system is reclaiming the memory the activity is using.
Figure 4.7 illustrates the various states the activity goes through and the order in which the callback methods get invoked.
Figure 4.7 Activity lifecycle showing activity states
Exploring the Android Activity Lifecycle
Now let’s look at how the Android activity lifecycle works. In Chapter 3, you overrode the onCreate method. Now you’ll override the remaining lifecycle methods in your TimerActivity class by following these steps.
Open the TimerActivity.java file in the project, and override the existing onStart method, which is called when the activity is first viewed. Call the onStart method of the parent class, and log a debug message:
Override the existing onPause method, which is called when another activity is called to the foreground. Call the onPause method of the parent and log a debug message:
Override the existing onResume method, which is called when the activity is running in the foreground and the user can interact with it. Call the onResume method of the parent class, and log a debug message:
Override the existing onStop method, which is called when the activity is invisible to the end user. Call the onStop method of the parent class, and log a debug message:
Override the existing onDestroy method, which is called when the activity is removed from the system and can no longer be interacted with. Call the onDestroy method of the parent class, and log a debug message:
Override the existing onRestart method, which is called when the activity is started again and returns to the foreground. Call the onRestart method of the parent class and log a debug message:
Now debug your application on a device, and look at the debug messages (in the LogCat view) that show the changes of state in the application, as shown in Figure 4.8. Experiment with the application to see which state changes occur.
- Turn your device on its side to see if the state changes. The activity is re-created when you do this, and in that process it loses all state.
- Navigate to another application, and see which methods are called.
- Let your device go to sleep, and then unlock the screen to see your application again.
Figure 4.8 LogCat showing activity lifecycle
Fixing Activity Lifecycle Issues
As you’ve seen, when the application is not running there is no need to have the timer display update, and when the timer activity is re-created you need to refresh the display to put it into the correct state.
To fix these issues you need to update the screen at the correct time.
When onStart is called and the timer is still running, start calling the run method of UpdateTimer again. Add this code to the onStart method:
updateTimer, UPDATE_EVERY); }
When onStop is called, you no longer need to update the display. Add this code to the onStop method:
When onResume is called, you need to refresh the display. Add these two lines of code:
- Debug the application on a device, and rotate the screen when the timer is running. You should now see that the application behaves as you would expect.