Difference between revisions of "Android Activity State Changes - An Example Application"
m (Text replacement - "<google>BUY_ANDROID</google>" to "<htmlet>androidstudio</htmlet>") |
m (Text replacement - "<table border="0" cellspacing="0">" to "<table border="0" cellspacing="0" width="100%">") |
||
(6 intermediate revisions by the same user not shown) | |||
Line 8: | Line 8: | ||
− | <htmlet> | + | <htmlet>android44</htmlet> |
Line 44: | Line 44: | ||
At this point, the project contains a single activity named StateChangeActivity, which is, as with most activities, derived from the Android Activity class. The source code for this activity is contained within the StateChangeActivity.java file. Navigate to this file in the Package Explorer panel (StateChange -> src -> com.yourcompany.StateChange -> StateChangeActivity.java) and double click on it to load the file into the editor pane. Once loaded the code should read as follows: | At this point, the project contains a single activity named StateChangeActivity, which is, as with most activities, derived from the Android Activity class. The source code for this activity is contained within the StateChangeActivity.java file. Navigate to this file in the Package Explorer panel (StateChange -> src -> com.yourcompany.StateChange -> StateChangeActivity.java) and double click on it to load the file into the editor pane. Once loaded the code should read as follows: | ||
− | <htmlet> | + | <htmlet>android44</htmlet> |
<pre> | <pre> | ||
Line 250: | Line 250: | ||
− | <htmlet> | + | <htmlet>android44</htmlet> |
+ | |||
+ | <htmlet>ezoicbottom</htmlet> | ||
<hr> | <hr> | ||
<table border="0" cellspacing="0" width="100%"> | <table border="0" cellspacing="0" width="100%"> |
Latest revision as of 19:58, 27 October 2016
Previous | Table of Contents | Next |
Handling Android Activity State Changes | Saving and Restoring the User Interface State of an Android Activity |
The previous chapters have discussed in some detail the different states and lifecycles of the activities that comprise an Android application. In this chapter, we will put the theory of handling activity state changes into practice through the creation of an example application. The purpose of this example application is to provide a real world demonstration of an activity as it passes through a variety of different states within the Android runtime, and to highlight the implications of failing to adequately save and restore state.
In the next chapter, entitled Saving and Restoring the User Interface State of an Android Activity, the example project constructed in this chapter will be extended to demonstrate the saving and restoration of dynamic user interface state.
Creating the State Change Example Project
The first step in this exercise is to create the new project. To this end, launch Eclipse and select the File -> New -> Android Application Project. Within the resulting new project dialog, name both the application and project StateChange. Enter the package name using your reverse domain name and the application name as outlined in Creating an Example Android Application (for example com.mycompany.statechange), or use the default com.example temporary name if you do not yet have a domain name. Select the latest SDK for the Minimum SDK Required, Target SDK and Compile With settings.
Continue to click the Next button to work through the screens. On the Configure Project screen, leave the Create custom launcher icon and Create activity options selected. Use the default Android icon on the Configure Launcher Icon screen and make sure BlankActivity is selected on the Create Activity screen. Finally, on the Blank Activity screen, name the Activity StateChangeActivity, and the layout activity_state_change and the fragment fragment_state_change before clicking on Finish.
Upon completion of the project creation process, the StateChange project should be listed in the Package Explorer panel located along the left-hand edge of the Eclipse main window:
Figure 10-1
Navigate to StateChange -> res -> layout -> fragment_state_change.xml in the Package Explorer panel and double click on it to load the user interface file for the activity into the Graphical Layout tool.
Designing the User Interface
With the project created, it is now time to design the user interface for the example application. By default, however, the Graphical Layout tool is displaying a design canvas for the Nexus One device. Using the toolbar menu within the Graphical Layout tool panel, click on Nexus One to display the drop down menu and change the device to a different model if required. Using the menu to the right of the device menu, change the orientation from Landscape to Portrait if necessary.
Instead of the TextView currently present in the user interface design, the activity actually requires an EditText view. Select the TextView object in the Graphical Layout canvas and press the Delete key on the keyboard to remove it from the design. Select the Text Fields category from the palette and drag and drop an Plain Text EditText widget into the center of the design layout.
When working with EditText views in an Android user interface it is necessary to declare the input type for the view. This simply defines the type of text or data that will be entered by the user. For example, if the input type is set to Phone, the user will be restricted to entering numerical digits into the view. Alternatively, if the input type is set to TextCapCharacters, the input will default to upper case characters. Input type settings may also be combined.
For the purposes of this example, we will set the input type to support general text input. To do so, right-click on the EditText view and select Input Type -> Text from the resulting menu. Remember to save the file before continuing to the next step.
Overriding the Activity Lifecycle Methods
At this point, the project contains a single activity named StateChangeActivity, which is, as with most activities, derived from the Android Activity class. The source code for this activity is contained within the StateChangeActivity.java file. Navigate to this file in the Package Explorer panel (StateChange -> src -> com.yourcompany.StateChange -> StateChangeActivity.java) and double click on it to load the file into the editor pane. Once loaded the code should read as follows:
package com.example.statechange; import android.app.Activity; import android.app.ActionBar; import android.app.Fragment; import android.os.Bundle; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.os.Build; public class StateChangeActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_state_change); if (savedInstanceState == null) { getFragmentManager().beginTransaction() .add(R.id.container, new PlaceholderFragment()) .commit(); } } . . . }
So far the only lifecycle method overridden by the activity is the onCreate() method which has been implemented to call the super class instance of the method before setting up the user interface for the activity. We will now modify this method so that it outputs a diagnostic message in the Eclipse LogCat panel each time it executes. For this, we will use the Log class, which requires that we import android.util.Log and declare a tag that will enable us to filter these messages in the log output:
package com.example.statechange; import android.app.Activity; import android.app.ActionBar; import android.app.Fragment; import android.os.Bundle; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.os.Build; import android.util.Log; public class StateChangeActivity extends Activity { private static final String TAG = "com.example.StateChange"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_state_change); if (savedInstanceState == null) { getFragmentManager().beginTransaction() .add(R.id.container, new PlaceholderFragment()) .commit(); } Log.i(TAG, "onCreate"); } . . . }
The next task is to override some more methods, with each one containing a corresponding log call:
protected void onStart() { super.onStart(); Log.i(TAG, "onStart"); } protected void onResume() { super.onResume(); Log.i(TAG, "onResume"); } protected void onPause() { super.onPause(); Log.i(TAG, "onPause"); } protected void onStop() { super.onStop(); Log.i(TAG, "onStop"); } protected void onRestart() { super.onRestart(); Log.i(TAG, "onRestart"); } protected void onDestroy() { super.onDestroy(); Log.i(TAG, "onDestroy"); } protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); Log.i(TAG, "onSaveInstanceState"); } protected void onRestoreInstanceState(Bundle savedState) { Log.i(TAG, "onRestoreInstanceState"); }
Enabling and Filtering the LogCat Panel
The purpose of the code added to the overridden methods in StateChangeActivity.java is to output logging information to the LogCat panel within the main Eclipse window. By default, this panel is not displayed so it will be necessary to configure Eclipse to display it before running the application.
To enable LogCat, select the Window -> Show View -> Other… menu option. In the Show View dialog, select LogCat from the Android category, and click on OK. An additional tab labeled LogCat should now be present next to the Console tab in the bottom panel of the Eclipse window as shown in Figure 10-2:
Figure 10-2
With no filters defined, the log will list every logged event that takes place within the device or emulator thereby making it all but impossible to find the log messages for our overridden methods. Before running the application, therefore, it is necessary to create a filter which, when selected, will ensure that only log messages that contain the tag declared in our activity are displayed.
With the LogCat panel displayed, click on the green ‘+’ located to the right of the Saved Filters title to display the filter settings dialog (Figure 10 3). Name the filter Lifecycle and in the by Log Tag field, enter the Tag value declared in StateChangeActivity.java (in the above code example this was com.example.StateChange but you may have changed this to reflect your company’s URL).
Figure 10-3
When the changes are complete, click on the OK button to create the filter and dismiss the dialog.
Running the Application
For optimal results, the application should be run on a physical Android device, details of which can be found in the chapter entitled Testing Android Applications on a Physical Android Device with ADB. With the device configured and connected to the development computer, right-click on the StateChange entry in the Package Explorer panel and select Run As -> Android Application. Select the physical Android device from the Android Device Chooser dialog if it appears (assuming that you have not already configured it to be the default target) and then, with the Android device still connected to the computer, take the usual steps to wake up and access the device. At this point, the application should be running in the foreground.
A review of the LogCat panel should indicate which methods have so far been triggered (taking care to ensure that the Lifecycle filter created in the preceding section is selected to filter out log events that are not currently of interest to us):
Figure 10-4
Experimenting with the Activity
With the diagnostics working, it is now time to exercise the application with a view to gaining an understanding of the activity lifecycle state changes. To begin with, consider the initial sequence of log events in the LogCat panel:
onCreate onStart onResume onSaveInstanceState onPause onResume
Clearly, the initial state changes are exactly as outlined in Figure 10-1. Note, however, that a call was not made to onRestoreInstanceState() since the Android runtime detected that there was no state to restore in this situation. The subsequent calls to onSaveInstanceState(), onPause() and onResume() are slightly less obvious at first glance. Consider, however, the sequence of steps performed in the previous section. The application was actually launched while the device display was turned off. The runtime, therefore, detected that the activity was not visible to the user and, as a result, paused the activity as soon as it was appropriate to do so. When the device was woken up and the activity became visible, the onResume() method was called to let the activity know.
Tap on the Home icon in the bottom status bar on the device display and note the sequence of method calls reported in the log as follows:
onSaveInstanceState onPause onStop
In this case, the runtime has noticed that the activity is no longer in the foreground, is not visible to the user and has stopped the activity, but not without providing an opportunity for the activity to save the dynamic state. Depending on whether the runtime ultimately destroyed the activity or simply restarted it, the activity will either be notified it has been restarted via a call to onRestart() or will go through the creation sequence again when the user returns to the activity.
As outlined in Understanding Android Application and Activity Lifecycles, the destruction and recreation of an activity can be triggered by making a configuration change to the device, such as rotating from portrait to landscape. To see this in action, simply rotate the device while the StateChange application is in the foreground. When using the emulator, device rotation may be simulated using the Ctrl-F12 keyboard shortcut or by pressing the number 7 on the keyboard keypad while Num Lock is off. The resulting sequence of method calls in the log should read as follows:
onSaveInstanceState onPause onStop onDestroy onCreate onStart onRestoreInstanceState onResume
Clearly, the runtime system has given the activity an opportunity to save state before destroying and restarting the activity.
Saving Dynamic State
Summary
The old adage that a picture is worth a thousand words holds just as true for examples when learning a new programming paradigm. In this chapter, we have created an example Android application for the purpose of demonstrating the different lifecycle states through which an activity is likely to pass. The example was then used to highlight the importance of saving and restoring dynamic state in order to provide the user with a consistent and seamless user interface experience. In the course of developing the project in this chapter, we also looked at a mechanism for generating diagnostic logging information from within an activity.
In the next chapter, we will extend the StateChange example project to demonstrate how to save and restore an activity’s dynamic state.
Previous | Table of Contents | Next |
Handling Android Activity State Changes | Saving and Restoring the User Interface State of an Android Activity |