Android Activity State Changes - An Example Application

From Techotopia
Revision as of 18:12, 11 May 2016 by Neil (Talk | contribs) (Text replacement - "<hr> <table border=" to "<htmlet>ezoicbottom</htmlet> <hr> <table border=")

Jump to: navigation, search
PreviousTable of ContentsNext
Handling Android Activity State ChangesSaving and Restoring the User Interface State of an Android Activity


You are currently reading the Eclipse - Android 4.4 Edition of this book.

Purchase the fully updated Android Studio 3.0 / Android 8 Edition of this publication in eBook ($24.99) or Print ($39.99) format

Android Studio 3.0 Development Essentials - Android 8 Edition Print and eBook (ePub/PDF/Kindle) editions contain 82 chapters and over 810 pages

Buy eBook Buy Print Preview Book


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:

The user interface for an Android State Change example app

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:

You are currently reading the Eclipse - Android 4.4 Edition of this book.

Purchase the fully updated Android Studio 3.0 / Android 8 Edition of this publication in eBook ($24.99) or Print ($39.99) format

Android Studio 3.0 Development Essentials - Android 8 Edition Print and eBook (ePub/PDF/Kindle) editions contain 82 chapters and over 810 pages

Buy eBook Buy Print Preview Book

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:


The Eclipse LogCat panel for the Android state change example app

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).


Configuring a LogCat filter

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):


The LogCat output from the running application

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

So far in this example, no steps have been taken to save the dynamic state of the activity. Although appropriate methods have been overridden, no code other than a log output has been implemented. The importance of implementing this functionality can be demonstrated using the example application. With the device in portrait orientation and the activity in the foreground, touch inside the EditText field and enter some new text into the box. Make a mental note of the change made to the text before rotating the device into landscape orientation. As should be evident, the changes made to the text have been lost. This is, quite simply, because no steps have been taken to ensure that state is saved and restored in the event that the activity is destroyed and recreated. This will be the focus of the next chapter, entitled Saving and Restoring the User Interface State of an Android Activity.

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.


You are currently reading the Eclipse - Android 4.4 Edition of this book.

Purchase the fully updated Android Studio 3.0 / Android 8 Edition of this publication in eBook ($24.99) or Print ($39.99) format

Android Studio 3.0 Development Essentials - Android 8 Edition Print and eBook (ePub/PDF/Kindle) editions contain 82 chapters and over 810 pages

Buy eBook Buy Print Preview Book



PreviousTable of ContentsNext
Handling Android Activity State ChangesSaving and Restoring the User Interface State of an Android Activity