Handling Android Activity State Changes
Previous | Table of Contents | Next |
Understanding Android Application and Activity Lifecycles | Android Activity State Changes – An Example Application |
<google>BUY_KINDLE_FIRE</google>
Based on the information outlined in the chapter entitled Understanding Android Application and Activity Lifecycles it is now evident that the activities that make up an application pass through a variety of different states during the course of the application’s lifespan. The change from one state to the other is imposed by the Android runtime system and is, therefore, largely beyond the control of the activity itself. That said, in most instances the runtime will provide the activity in question with a notification of the state change thereby giving it time to react accordingly. In most cases this will involve saving or restoring data relating to the state of the activity and its user interface.
The primary objective of this chapter is to provide a high level overview of the ways in which an activity may be notified of a state change and to outline the areas where it is advisable to save or restore state information. Having covered this information, the chapter will then touch briefly on the subject of activity lifetimes.
The Activity Class
All the activities in an application are created as subclasses of the Android Activity class. Consider, for example, the simple KindleTest project created in Creating an Example Kindle Fire Android Application. Load this project into the Eclipse IDE environment and navigate in the left hand panel to the KindleTestActvity.java file (located in KindleTest -> src -> com.yourcompany.KindleTest). Having located the file, double click on it to load it into the editor panel where it should read as follows:
package com.payloadmedia.KindleTest; import android.app.Activity; import android.os.Bundle; public class KindleTestActivity extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); } }
When the project was initially created, we instructed Eclipse to also create an initial activity named KindleTestActivity. As is evident from the above code, the KindleTestActivity class extends, and is therefore a subclass of, the Activity class. The Activity class contains a range of methods which are intended to be called by the Android runtime to notify an activity that its state is changing. For the purposes of this chapter we will refer to these as the activity lifecycle methods. An activity class simply needs to override these methods and implement the necessary functionality within them in order to react accordingly to state changes.
One such method is named onCreate() and, turning once again to the above code fragment, we can see that this method has already been overridden and implemented for us in the KindleTestActivity class. In a later section we will explore in detail both onCreate() and the other relevant lifecycle methods of the Activity class.
Dynamic State vs. Persistent State
A key objective of Activity lifecycle management is ensuring that the state of the activity gets saved and restored at appropriate times. When talking about state in this context we mean the data that is currently being held within the activity and the appearance of the user interface. The activity might, for example, maintain a data model in memory that needs to be saved to a database, content provider or file. Such state information, because it persists from one invocation of the application to another, is referred to as the persistent state.
The appearance of the user interface (such as text entered into a text field but not yet committed to the application’s internal data model) is referred to as the dynamic state, since it is typically only retained during a single invocation of the application (and also referred to as user interface state).
Understanding the differences between these two states is important because both the ways they are saved, and the reasons for doing so, differ.
The purpose of saving the persistent state is to avoid the loss of data that may result from an activity being killed by the runtime system while in the background. The dynamic state, on the other hand, is saved and restored for slightly more complex reasons.
Consider, for the sake of an example, that an application contains an activity (which we will refer to as Activity A) containing a text field and some radio buttons. During the course of using the application, the user enters some text into the text field and makes a selection from the radio buttons. Before performing an action to save these changes, however, the user then switches to another activity causing Activity A to be pushed down the Activity Stack and placed into the background. After some time, the runtime system ascertains that memory is low and consequently kills Activity A to free up resources. As far as the user is concerned, however, Activity A was simply placed into the background and is ready to be moved to the foreground at any time. On returning Activity A to the foreground the user would, quite reasonably, expect the entered text and radio button selections to have been retained. In this scenario, however, a new instance of Activity A will have been created and, if the dynamic state was not saved and restored, the previous user inputs lost.
The main purpose of saving dynamic state, therefore, is to give the perception of seamless switching between foreground and background activities, regardless of the fact that activities may actually have been killed and restarted without the user’s knowledge.
The mechanisms for saving persistent and dynamic state will become clearer in the following sections of this chapter.
The Android Activity Lifecycle Methods
As previously explained, the Activity class contains a number of lifecycle methods which act as event handlers when the state of an Activity changes. The primary methods supported by the Android Activity class are as follows:
- onCreate(Bundle savedInstanceState) – The method that is called when the activity is first created and the ideal location for most initialization tasks to be performed. The method is passed an argument in the form of a Bundle object which may contain dynamic state information (typically relating the state of the user interface) from a prior invocation of the activity.
- onRestart() – Called when the activity is about to restart after having previously been stopped by the runtime system.
- onStart() – Always called immediately after the call to the onCreate() or onRestart() methods, this method indicates to the activity that it is about to become visible to the user. This call will be followed by a call to onResume() if the activity moves to the top of the activity stack, or onStop() in the event that it is pushed down the stack by another activity.
- onResume() – Indicates that the activity is now at the top of the activity stack and is the activity with which the user is currently interacting.
- onPause() – Indicates that a previous activity is about to become the foreground activity. This call will be followed by a call to either the onResume() or onStop() method depending on whether the activity moves back to the foreground or become invisible to the user. Steps should be taken within this method to store any persistent data required by the activity (such as data stored to a content provider, database or file). This method should also ensure than any CPU intensive tasks such as animation are stopped.
- onStop() – The activity is now no longer visible to the user. The two possible scenarios that may follow this call are a call to onRestart() in the event that the activity moves to the foreground again, or onDestroy() if the activity is being terminated.
- onDestroy() – The activity is about to be destroyed, either voluntarily because the activity has completed its tasks and has called the finish() method or because the runtime is terminating it either to release memory or due to a configuration change (such as the orientation of the device changing). It is important to note that a call will not always be made to onDestroy() when an activity is terminated.
In addition to the lifecycle methods outlined above, there are two methods intended specifically for saving and restoring the dynamic state of an activity:
- onRestoreInstanceState(Bundle savedInstanceState) – This method is called immediately after a call to the onStart() method in the event that the activity is restarting from a previous invocation in which state was saved. As with onCreate(), this method is passed a Bundle object containing the previous state data. This method is typically used in situations where it makes more sense to restore a previous state after the initialization of the activity has been performed in onCreate() and onStart().
- onSaveInstanceState(Bundle outState) – Called before an activity is destroyed so that the current dynamic state (usually relating to the user interface) can be saved. The method is passed the Bundle object into which the state should be saved and which is subsequently passed through to the onCreate() and onRestoreInstanceState() methods when the activity is restarted. Note that this method is only called in situations where the runtime ascertains that dynamic state needs to be saved.
When overriding the above methods in an activity, it is important to remember that with the exception of onRestoreInstanceState() and onSaveInstanceState(), the method implementation must include a call to the corresponding method in the Activity super class. For example, the following method overrides the onRestart() methods but also includes a call to the super class instance of the method:
protected void onRestart() { super.onRestart(); Log.i(TAG, "onRestart"); }
Failure to make this super class call in method overrides will result in the runtime throwing an exception during execution of the activity.
Activity Lifetimes
The final topic to be covered involves an outline of the entire, visible and foreground lifetimes through which an activity will transition during execution:
- Entire Lifetime –The term “entire lifetime” is used to describe everything that takes place within an activity between the initial call to the onCreate() method and the call to onDestroy() prior to the activity terminating.
- Visible Lifetime – Covers the periods of execution of an activity between the call to onStart() and onStop(). During this period the activity is visible to the user though may not be the activity with which the user is currently interacting.
- Foreground Lifetime – Refers to the periods of execution between calls to the onResume()and onPause() methods.
It is important to note that an activity may pass through the foreground and visible lifetimes multiple times during the course of the entire lifetime. The concepts of lifetimes and lifecycle methods are illustrated in Figure 9-1:
Figure 9-1
Summary
All activities are derived from the Android Activity class which, in turn, contains a number of event methods that are designed to be called by the runtime system when the state of an activity changes. By overriding these methods, an activity can respond to state changes and, where necessary, take steps to save and restore the current state of both the activity and the application. Activity state can be thought of as taking two forms. The persistent state refers to data that needs to be stored between application invocations (for example to a file or database). Dynamic state, the other hand, relates instead to the current appearance of the user interface.
In this chapter we have highlighted the lifecycle methods available to activities and covered the concept of activity lifetimes. In the next chapter, entitled Android Activity State Changes – An Example Application, we will implement an example application that puts much of this theory into practice.
<google>BUY_KINDLE_FIRE</google>
Previous | Table of Contents | Next |
Understanding Android Application and Activity Lifecycles | Android Activity State Changes – An Example Application |