Creating Applications and Activities - Professional Android 4 Application Development (2012)

Professional Android 4 Application Development (2012)

Chapter 3. Creating Applications and Activities

What's in this Chapter?

Introducing the Android application components and the different types of applications you can build with them

Understanding the Android application lifecycle

Creating your application manifest

Using external resources to provide dynamic support for locations, languages, and hardware configurations

Implementing and using your own Application class

Creating new Activities

Understanding an Activity's state transitions and lifecycle

To write high-quality applications, it's important to understand the components they consist of and how those components are bound together by the Android manifest. This chapter introduces each of the application components, with special attention paid to Activities.

Next, you'll see why and how you should use external resources and the resource hierarchy to create applications that can be customized and optimized for a variety of devices, countries, and languages.

In Chapter 2, “Getting Started,” you learned that each Android application runs in a separate process, in its own instance of the Dalvik virtual machine. In this chapter, you learn more about the application lifecycle and how the Android run time can manage your application. You are also introduced to the application and Activity states, state transitions, and event handlers. The application's state determines its priority, which, in turn, affects the likelihood of its being terminated when the system requires more resources.

You should always provide the best possible experience for users, no matter which country they're in or which of the wide variety of Android device types, form factors, and screen sizes they're using. In this chapter, you learn how to use the resource framework to provide optimized resources, ensuring your applications run seamlessly on different hardware (particularly different screen resolutions and pixel densities), in different countries, and supporting multiple languages.

The Activity class forms the basis for all your user interface (UI) screens. You learn how to create Activities and gain an understanding of their lifecycles and how they affect the application lifetime and priority.

Finally, you are introduced to some of the Activity subclasses that simplify resource management for some common UI patterns, such as map- and list-based Activities.

What Makes an Android Application?

Android applications consist of loosely coupled components, bound by the application manifest that describes each component and how they interact. The manifest is also used to specify the application's metadata, its hardware and platform requirements, external libraries, and required permissions.

The following components comprise the building blocks for all your Android applications:

· Activities—Your application's presentation layer. The UI of your application is built around one or more extensions of the Activity class. Activities use Fragments and Views to layout and display information, and to respond to user actions. Compared to desktop development, Activities are equivalent to Forms. You'll learn more about Activities later in this chapter.

· Services—The invisible workers of your application. Service components run without a UI, updating your data sources and Activities, triggering Notifications, and broadcasting Intents. They're used to perform long running tasks, or those that require no user interaction (such as network lookups or tasks that need to continue even when your application's Activities aren't active or visible.) You'll learn more about how to create and use services in Chapter 9, “Working in the Background.”

· Content Providers—Shareable persistent data storage. Content Providers manage and persist application data and typically interact with SQL databases. They're also the preferred means to share data across application boundaries. You can configure your application's Content Providers to allow access from other applications, and you can access the Content Providers exposed by others. Android devices include several native Content Providers that expose useful databases such as the media store and contacts. You'll learn how to create and use Content Providers in Chapter 8, “Databases and Content Providers.”

· Intents—A powerful interapplication message-passing framework. Intents are used extensively throughout Android. You can use Intents to start and stop Activities and Services, to broadcast messages system-wide or to an explicit Activity, Service, or Broadcast Receiver, or to request an action be performed on a particular piece of data. Explicit, implicit, and broadcast Intents are explored in more detail in Chapter 5, “Intents and Broadcast Receivers.”

· Broadcast Receivers—Intent listeners. Broadcast Receivers enable your application to listen for Intents that match the criteria you specify. Broadcast Receivers start your application to react to any received Intent, making them perfect for creating event-driven applications. Broadcast Receivers are covered with Intents in Chapter 5.

· Widgets—Visual application components that are typically added to the device home screen. A special variation of a Broadcast Receiver, widgets enable you to create dynamic, interactive application components for users to embed on their home screens. You'll learn how to create your own widgets in Chapter 14, “Invading the Home Screen.”

· Notifications—Notifications enable you to alert users to application events without stealing focus or interrupting their current Activity. They're the preferred technique for getting a user's attention when your application is not visible or active, particularly from within a Service or Broadcast Receiver. For example, when a device receives a text message or an email, the messaging and Gmail applications use Notifications to alert you by flashing lights, playing sounds, displaying icons, and scrolling a text summary. You can trigger these notifications from your applications, as discussed in Chapter 10, “Expanding the User Experience.”

By decoupling the dependencies between application components, you can share and use individual Content Providers, Services, and even Activities with other applications—both your own and those of third parties.

Introducing the Application Manifest File

Each Android project includes a manifest file, AndroidManifest.xml, stored in the root of its project hierarchy. The manifest defines the structure and metadata of your application, its components, and its requirements.

It includes nodes for each of the Activities, Services, Content Providers, and Broadcast Receivers that make up your application and, using Intent Filters and Permissions, determines how they interact with each other and with other applications.

The manifest can also specify application metadata (such as its icon, version number, or theme), and additional top-level nodes can specify any required permissions, unit tests, and define hardware, screen, or platform requirements (as described next).

The manifest is made up of a root manifest tag with a package attribute set to the project's package. It should also include an xmlns:android attribute that supplies several system attributes used within the file.

Use the versionCode attribute to define the current application version as an integer that increases with each version iteration, and use the versionName attribute to specify a public version that will be displayed to users.

You can also specify whether to allow (or prefer) for your application be installed on external storage (usually an SD card) rather than internal storage using the installLocation attribute. To do this specify either preferExternal or auto, where the former installs to external storage whenever possible, and the latter asks the system to decide.

2.1

If your application is installed on external storage, it will be immediately killed if a user mounts the USB mass storage to copy files to/from a computer, or ejects or unmounts the SD card.

If you don't specify an install location attribute, your application will be installed in the internal storage and users won't be able to move it to external storage. The total amount of internal storage is generally limited, so it's good practice to let your application be installed on external storage whenever possible.

There are some applications for which installation to external storage is not appropriate due to the consequences of unmounting or ejecting the external storage, including:

· Applications with Widgets, Live Wallpapers, and Live Folders—Your Widgets, Live Wallpapers, and Live Folders will be removed from the home screen and may not be available until the system restarts.

· Applications with ongoing Services—Your application and its running Services will be stopped and won't be restarted automatically.

· Input Method Engines—Any IME installed on external storage will be disabled and must be reselected by the user after the external storage is once again available.

· Device administrators—Your DeviceAdminReceiver and any associated admin capabilities will be disabled.

A Closer Look at the Application Manifest

The following XML snippet shows a typical manifest node:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.paad.myapp"
          android:versionCode="1"
          android:versionName="0.9 Beta"
          android:installLocation="preferExternal">
          [ ... manifest nodes ... ]
</manifest>

The manifest tag can include nodes that define the application components, security settings, test classes, and requirements that make up your application. The following list gives a summary of the available manifest sub-node tags and provides an XML snippet demonstrating how each tag is used:

· uses-sdk—This node enables you to define a minimum and maximum SDK version that must be available on a device for your application to function properly, and target SDK for which it has been designed using a combination of minSDKVersion, maxSDKVersion, and targetSDKVersionattributes, respectively.

The minimum SDK version specifies the lowest version of the SDK that includes the APIs you have used in your application. If you fail to specify a minimum version, it defaults to 1, and your application crashes when it attempts to access unavailable APIs.

The target SDK version attribute enables you to specify the platform against which you did your development and testing. Setting a target SDK version tells the system that there is no need to apply any forward- or backward-compatibility changes to support that particular version. To take advantage of the newest platform UI improvements, it's considered good practice to update the target SDK of your application to the latest platform release after you confirm it behaves as expected, even if you aren't making use of any new APIs.

It is usually unnecessary to specify a maximum SDK, and doing so is strongly discouraged. The maximum SDK defines an upper limit you are willing to support and your application will not be visible on the Google Play Store for devices running a higher platform release. Devices running on platforms higher than Android 2.0.1 (API level 6) will ignore any maximum SDK values at installation time.

<uses-sdk android:minSdkVersion="6"
          android:targetSdkVersion="15"/>

2.1

The supported SDK version is not equivalent to the platform version and cannot be derived from it. For example, Android platform release 4.0 supports the SDK version 14. To find the correct SDK version for each platform, use the table athttp://developer.android.com/guide/appendix/api-levels.html.

· uses-configuration—The uses-configuration nodes specify each combination of input mechanisms are supported by your application. You shouldn't normally need to include this node, though it can be useful for games that require particular input controls. You can specify any combination of input devices that include the following:

· reqFiveWayNav—Specify true for this attribute if you require an input device capable of navigating up, down, left, and right and of clicking the current selection. This includes both trackballs and directional pads (D-pads).

· reqHardKeyboard—If your application requires a hardware keyboard, specify true.

· reqKeyboardType—Lets you specify the keyboard type as one of nokeys, qwerty, twelvekey, or undefined.

· reqNavigation—Specify the attribute value as one of nonav, dpad, trackball, wheel, or undefined as a required navigation device.

· reqTouchScreen—Select one of notouch, stylus, finger, or undefined to specify the required touchscreen input.

You can specify multiple supported configurations, for example, a device with a finger touchscreen, a trackball, and either a QUERTY or a twelve-key hardware keyboard, as shown here:

<uses-configuration android:reqTouchScreen="finger"
                    android:reqNavigation="trackball"
                    android:reqHardKeyboard="true"
                    android:reqKeyboardType="qwerty"/>
<uses-configuration android:reqTouchScreen="finger"
                    android:reqNavigation="trackball"
                    android:reqHardKeyboard="true"
                    android:reqKeyboardType="twelvekey"/>

2.1

When specifying required configurations, be aware that your application won't be installed on any device that does not have one of the combinations specified. In the preceding example, a device with a QWERTY keyboard and a D-pad (but no touchscreen or trackball) would not be supported. Ideally, you should develop your application to ensure it works with any input configuration, in which case no uses-configuration node is required.

· uses-feature —Android is available on a wide variety of hardware platforms. Use multiple uses-feature nodes to specify which hardware features your application requires. This prevents your application from being installed on a device that does not include a required piece of hardware, such as NFC hardware, as follows:

<uses-feature android:name="android.hardware.nfc" />

You can require support for any hardware that is optional on a compatible device. Currently, optional hardware features include the following:

· Audio—For applications that requires a low-latency audio pipeline. Note that at the time of writing this book, no Android devices satisfied this requirement.

· Bluetooth—Where a Bluetooth radio is required.

· Camera—For applications that require a camera. You can also require (or set as options) autofocus, flash, or a front-facing camera.

· Location—If you require location-based services. You can also specify either network or GPS support explicitly.

· Microphone—For applications that require audio input.

· NFC—Requires NFC (near-field communications) support.

· Sensors—Enables you to specify a requirement for any of the potentially available hardware sensors.

· Telephony—Specify that either telephony in general, or a specific telephony radio (GSM or CDMA) is required.

· Touchscreen—To specify the type of touch-screen your application requires.

· USB—For applications that require either USB host or accessory mode support.

· Wi-Fi—Where Wi-Fi networking support is required.

As the variety of platforms on which Android is available increases, so too will the optional hardware. You can find a full list of uses-feature hardware at http://developer.android.com/guide/topics/manifest/uses-feature-element.html#features-reference.

To ensure compatibility, requiring some permissions implies a feature requirement. In particular, requesting permission to access Bluetooth, the camera, any of the location service permissions, audio recording, Wi-Fi, and telephony-related permissions implies the corresponding hardware features. You can override these implied requirements by adding a required attribute and setting it to false—for example, a note-taking application that supports recording an audio note:

<uses-feature android:name="android.hardware.microphone" 
              android:required="false" />

The camera hardware also represents a special case. For compatibility reasons requesting permission to use the camera, or adding a uses-feature node requiring it, implies a requirement for the camera to support autofocus. You can specify it as optional as appropriate:

<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus"
              android:required="false" />
<uses-feature android:name="android.hardware.camera.flash"
              android:required="false" />

You can also use the uses-feature node to specify the minimum version of OpenGL required by your application. Use the glEsVersion attribute, specifying the OpenGL ES version as an integer. The higher 16 bits represent the major number and the lower 16 bits represent the minor number, so version 1.1 would be represented as follows:

<uses-feature android:glEsVersion="0x0010001" />

· supports-screens—The first Android devices were limited to 3.2” HVGA hardware. Since then, hundreds of new Android devices have been launched including tiny 2.55” QVGA phones, 10.1” tablets, and 42” HD televisions. The supports-screen node enables you to specify the screen sizes your application has been designed and tested to. On devices with supported screens, your application is laid out normally using the scaling properties associated with the layout files you supply. On unsupported devices the system may apply a “compatibility mode,” such as pixel scaling to display your application. It's best practice to create scalable layouts that adapt to all screen dimensions.

You can use two sets of attributes when describing your screen support. The first set is used primarily for devices running Android versions prior to Honeycomb MR2 (API level 13). Each attribute takes a Boolean specifying support. As of SDK 1.6 (API level 4), the default value for each attribute is true, so use this node to specify the screen sizes you do not support.

· smallScreens—Screens with a resolution smaller than traditional HVGA (typically, QVGA screens).

· normalScreens—Used to specify typical mobile phone screens of at least HVGA, including WVGA and WQVGA.

· largeScreens—Screens larger than normal. In this instance a large screen is considered to be significantly larger than a mobile phone display.

· xlargeScreens—Screens larger than large-typically tablet devices.

Honeycomb MR2 (API level 13) introduced additional attributes that provide a finer level of control over the size of screen your application layouts can support. It is generally good practice to use these in combination with the earlier attributes if your application is available to devices running platform releases earlier than API level 13.

· requiresSmallestWidthDp—Enables you to specify a minimum supported screen width in device independent pixels. The smallest screen width for a device is the lower dimension of its screen height and width. This attribute can potentially be used to filter applications from the Google Play Store for devices with unsupported screens, so when used it should specify the absolute minimum number of pixels required for your layouts to provide a useable user experience.

· compatibleWidthLimitDp—Specifies the upper bound beyond which your application may not scale. This can cause the system to enable a compatibility mode on devices with screen resolutions larger than you specify.

· largestWidthLimitDp—Specifies the absolute upper bound beyond which you know your application will not scale appropriately. Typically this results in the system forcing the application to run in compatibility mode (without the ability for users to disable it) on devices with screen resolutions larger than that specified.

It is generally considered a bad user experience to force your application into compatibility mode. Wherever possible, ensure that your layouts scale in a way that makes them usable on larger devices.

<supports-screens android:smallScreens="false"
                  android:normalScreens="true"
                  android:largeScreens="true"
                  android:xlargeScreens="true"
                  android:requiresSmallestWidthDp="480"
                  android:compatibleWidthLimitDp="600"
                  android:largestWidthLimitDp="720"/>

2.1

Where possible you should optimize your application for different screen resolutions and densities using the resources folder, as shown later in this chapter, rather than enforcing a subset of supported screens.

· supports-gl-texture—Declares that the application is capable of providing texture assets that are compressed using a particular GL texture compression format. You must use multiple supports-gl-texture elements if your application is capable of supporting multiple texture compression formats. You can find the most up-to-date list of supported GL texture compression format values at http://developer.android.com/guide/topics/manifest/supports-gl-texture-element.html.

<supports-gl-texture android:name="GL_OES_compressed_ETC1_RGB8_texture" />

· uses-permission—As part of the security model, uses-permission tags declare the user permissions your application requires. Each permission you specify will be presented to the user before the application is installed. Permissions are required for many APIs and method calls, generally those with an associated cost or security implication (such as dialing, receiving SMS, or using the location-based services).

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>

· permission—Your application components can also create permissions to restrict access to shared application components. You can use the existing platform permissions for this purpose or define your own permissions in the manifest. To do this, use the permission tag to create a permission definition.

Your application components can then create permissions by adding an android:permission attribute. Then you can include a uses-permission tag in your manifest to use these protected components, both in the application that includes the protected component and any other application that wants to use it.

Within the permission tag, you can specify the level of access the permission permits (normal, dangerous, signature, signatureOrSystem), a label, and an external resource containing the description that explains the risks of granting the specified permission. More details on creating and using your own permissions can be found in Chapter 18, “Advanced Android Development.”

<permission android:name="com.paad.DETONATE_DEVICE"
            android:protectionLevel="dangerous"
            android:label="Self Destruct"
            android:description="@string/detonate_description">
</permission>

· instrumentation—Instrumentation classes provide a test framework for your application components at run time. They provide hooks to monitor your application and its interaction with the system resources. Create a new node for each of the test classes you've created for your application.

·                <instrumentation android:label="My Test"
·                                 android:name=".MyTestClass"
·                                 android:targetPackage="com.paad.apackage">
</instrumentation>

Note that you can use a period (.) as shorthand for prepending the manifest package to a class within your package.

· application—A manifest can contain only one application node. It uses attributes to specify the metadata for your application (including its title, icon, and theme). During development you should include a debuggable attribute set to true to enable debugging, then be sure to disable it for your release builds.

The application node also acts as a container for the Activity, Service, Content Provider, and Broadcast Receiver nodes that specify the application components. Later in this chapter you'll learn how to create and use your own Application class extension to manage application state. You specify the name of your custom application class using the android:name attribute.

<application android:icon="@drawable/icon"
             android:logo="@drawable/logo"
             android:theme="@android:style/Theme.Light"
             android:name=".MyApplicationClass"
             android:debuggable="true">
             [ ... application nodes ... ]
</application>

· activity—An activity tag is required for every Activity within your application. Use the android:name attribute to specify the Activity class name. You must include the main launch Activity and any other Activity that may be displayed. Trying to start an Activity that's not defined in the manifest will throw a runtime exception. Each Activity node supports intent-filter child tags that define the Intents that can be used to start the Activity. Later in this chapter you'll explore the Activity manifest entry in more detail.

Note, again, that a period is used as shorthand for the application's package name when specifying the Activity's class name.

<activity android:name=".MyActivity" android:label="@string/app_name">
  <intent-filter>
    <action android:name="android.intent.action.MAIN" />
    <category android:name=»android.intent.category.LAUNCHER» />
  </intent-filter>
</activity>

· service—As with the activity tag, add a service tag for each Service class used in your application. Service tags also support intent-filter child tags to allow late runtime binding.

·                <service android:name=".MyService">
</service>

· provider—Provider tags specify each of your application's Content Providers. Content Providers are used to manage database access and sharing.

·                <provider android:name=".MyContentProvider"
          android:authorities="com.paad.myapp.MyContentProvider"/>

· receiver—By adding a receiver tag, you can register a Broadcast Receiver without having to launch your application first. As you'll see in Chapter 5, Broadcast Receivers are like global event listeners that, when registered, will execute whenever a matching Intent is broadcast by the system or an application. By registering a Broadcast Receiver in the manifest you can make this process entirely autonomous. If a matching Intent is broadcast, your application will be started automatically and the registered Broadcast Receiver will be executed. Each receiver node supports intent-filter child tags that define the Intents that can be used to trigger the receiver:

·                <receiver android:name=".MyIntentReceiver">
·                  <intent-filter>
·                    <action android:name="com.paad.mybroadcastaction" />
·                  </intent-filter>
</receiver>

· uses-library—Used to specify a shared library that this application requires. For example, the maps APIs described in Chapter 13, “Maps, Geocoding, and Location-Based Services,” are packaged as a separate library that is not automatically linked. You can specify that a particular package is required—which prevents the application from being installed on devices without the specified library—or optional, in which case your application must use reflection to check for the library before attempting to make use of it.

·                <uses-library android:name="com.google.android.maps"
              android:required="false"/>

2.1

You can find a more detailed description of the manifest and each of these nodes at http://developer.android.com/guide/topics/manifest/manifest-intro.html.

The ADT New Project Wizard automatically creates a new manifest file when it creates a new project. You'll return to the manifest as each of the application components is introduced and explored.

Using the Manifest Editor

The Android Development Tools (ADT) plug-in includes a Manifest Editor, so you don't have to manipulate the underlying XML directly.

To use the Manifest Editor in Eclipse, right-click the AndroidManifest.xml file in your project folder, and select Open With → Android Manifest Editor. This presents the Android Manifest Overview screen, as shown in Figure 3.1. This screen gives you a high-level view of your application structure, enabling you to set your application version information and root level manifest nodes, including uses-sdk and uses-features, as described previously in this chapter. It also provides shortcut links to the Application, Permissions, Instrumentation, and raw XML screens.

Figure 3.1

3.1

Each of the next three tabs contains a visual interface for managing the application, security, and instrumentation (testing) settings, while the last tab (labeled with the manifest's filename) gives access to the underlying XML.

Of particular interest is the Application tab, as shown in Figure 3.2. Use it to manage the application node and the application component hierarchy, where you specify each of your application's components.

Figure 3.2

3.2

You can specify an application's attributes—including its icon, label, and theme—in the Application Attributes panel. The Application Nodes tree beneath it lets you manage the application components, including their attributes and any associated Intent Filters.

Externalizing Resources

It's always good practice to keep non-code resources, such as images and string constants, external to your code. Android supports the externalization of resources, ranging from simple values such as strings and colors to more complex resources such as images (Drawables), animations, themes, and menus. Perhaps the most powerful externalizable resources are layouts.

By externalizing resources, you make them easier to maintain, update, and manage. This also lets you easily define alternative resource values for internationalization and to include different resources to support variations in hardware—particularly, screen size and resolution.

You'll see later in this section how Android dynamically selects resources from resource trees that contain different values for alternative hardware configurations, languages, and locations. When an application starts, Android automatically selects the correct resources without you having to write a line of code.

Among other things, this lets you change the layout based on the screen size and orientation, images based on screen density, and customize text prompts based on a user's language and country.

Creating Resources

Application resources are stored under the res folder in your project hierarchy. Each of the available resource types is stored in subfolders, grouped by resource type.

If you start a project using the ADT Wizard, it creates a res folder that contains subfolders for the values, drawable-ldpi, drawable-mdpi, drawable-hdpi, and layout resources that contain the default string resource definitions, application icon, and layouts respectively, as shown in Figure 3.3.

Figure 3.3

3.3

Note that three drawable resource folders contain three different icons: one each for low, medium, and high density displays respectively.

Each resource type is stored in a different folder: simple values, Drawables, colors, layouts, animations, styles, menus, XML files (including searchables), and raw resources. When your application is built, these resources will be compiled and compressed as efficiently as possible and included in your application package.

This process also generates an R class file that contains references to each of the resources you include in your project. This enables you to reference the resources in your code, with the advantage of design-time syntax checking.

The following sections describe many of the specific resource types available within these categories and how to create them for your applications.

In all cases, the resource filenames should contain only lowercase letters, numbers, and the period (.) and underscore (_) symbols.

Simple Values

Supported simple values include strings, colors, dimensions, styles, and string or integer arrays. All simple values are stored within XML files in the res/values folder.

Within each XML file, you indicate the type of value being stored using tags, as shown in the sample XML file in Listing 3.1.

2.11

Listing 3.1: Simple values XML

<?xml version="1.0" encoding="utf-8"?>
<resources>
  <string name="app_name">To Do List</string>
  <plurals name="androidPlural">
    <item quantity="one">One android</item>
    <item quantity="other">%d androids</item>
  </plurals>
  <color name="app_background">#FF0000FF</color>
  <dimen name="default_border">5px</dimen>
  <string-array name="string_array">
    <item>Item 1</item>
    <item>Item 2</item>
    <item>Item 3</item>
  </string-array>
  <array name="integer_array">
    <item>3</item>
    <item>2</item>
    <item>1</item>
  </array>
</resources>

code snippet PA4AD_Ch03_Manifest_and_Resources/res/values/simple_values.xml

This example includes all the simple value types. By convention, resources are generally stored in separate files, one for each type; for example, res/values/strings.xml would contain only string resources.

The following sections detail the options for defining simple resources.

Strings

Externalizing your strings helps maintain consistency within your application and makes it much easier to internationalize them.

String resources are specified with the string tag, as shown in the following XML snippet:

<string name="stop_message">Stop.</string>

Android supports simple text styling, so you can use the HTML tags <b>, <i>, and <u> to apply bold, italics, or underlining, respectively, to parts of your text strings, as shown in the following example:

<string name="stop_message"><b>Stop.</b></string>

You can use resource strings as input parameters for the String.format method. However, String.format does not support the text styling previously described. To apply styling to a format string, you have to escape the HTML tags when creating your resource, as shown in the following snippet:

<string name="stop_message"><b>Stop</b>. %1$s</string>

Within your code, use the Html.fromHtml method to convert this back into a styled character sequence.

String rString = getString(R.string.stop_message);
String fString = String.format(rString, "Collaborate and listen.");
CharSequence styledString = Html.fromHtml(fString);

You can also define alternative plural forms for your strings. This enables you to define different strings based on the number of items you refer to. For example, in English you would refer to “one Android” or “seven Androids.”

By creating a plurals resource, you can specify an alternative string for any of zero, one, multiple, few, many, or other quantities. In English only the singular is a special case, but some languages require finer detail:

<plurals name="unicornCount">
  <item quantity="one">One unicorn</item>
  <item quantity="other">%d unicorns</item>
</plurals>

To access the correct plural in code, use the getQuantityString method on your application's Resources object, passing in the resource ID of the plural resource, and specifying the number of objects you want to describe:

Resources resources = getResources();
String unicornStr = resources.getQuantityString(
  R.plurals.unicornCount, unicornCount, unicornCount);

The object count is passed in twice—once to return the correct plural string, and again as an input parameter to complete the sentence.

Colors

Use the color tag to define a new color resource. Specify the color value using a # symbol followed by the (optional) alpha channel, and then the red, green, and blue values using one or two hexadecimal numbers with any of the following notations:

· #RGB

· #RRGGBB

· #ARGB

· #AARRGGBB

The following example shows how to specify a fully opaque blue and a partially transparent green:

<color name="opaque_blue">#00F</color>
<color name="transparent_green">#7700FF00</color>

Dimensions

Dimensions are most commonly referenced within style and layout resources. They're useful for creating layout constants, such as borders and font heights.

To specify a dimension resource, use the dimen tag, specifying the dimension value, followed by an identifier describing the scale of your dimension:

· px (screen pixels)

· in (physical inches)

· pt (physical points)

· mm (physical millimeters)

· dp (density-independent pixels)

· sp (scale-independent pixels)

Although you can use any of these measurements to define a dimension, it's best practice to use either density- or scale-independent pixels. These alternatives let you define a dimension using relative scales that account for different screen resolutions and densities to simplify scaling on different hardware.

Scale-independent pixels are particularly well suited when defining font sizes because they automatically scale if the user changes the system font size.

The following XML snippet shows how to specify dimension values for a large font size and a standard border:

<dimen name="standard_border">5dp</dimen>
<dimen name="large_font_size">16sp</dimen>

Styles and Themes

Style resources let your applications maintain a consistent look and feel by enabling you to specify the attribute values used by Views. The most common use of themes and styles is to store the colors and fonts for an application.

To create a style, use a style tag that includes a name attribute and contains one or more item tags. Each item tag should include a name attribute used to specify the attribute (such as font size or color) being defined. The tag itself should then contain the value, as shown in the following skeleton code.

<?xml version="1.0" encoding="utf-8"?>
<resources>
  <style name="base_text">
      <item name="android:textSize">14sp</item>
      <item name="android:textColor">#111</item>
  </style>
</resources>

Styles support inheritance using the parent attribute on the style tag, making it easy to create simple variations:

<?xml version="1.0" encoding="utf-8"?>
<resources>
  <style name="small_text" parent="base_text">
    <item name="android:textSize">8sp</item>
  </style>
</resources>

Drawables

Drawable resources include bitmaps and NinePatches (stretchable PNG images). They also include complex composite Drawables, such as LevelListDrawables and StateListDrawables, that can be defined in XML.

2.1

Both NinePatch Drawables and complex composite resources are covered in more detail in the next chapter.

All Drawables are stored as individual files in the res/drawable folder. Note that it's good practice to store bitmap image assets in the appropriate drawable -ldpi, -mdpi, -hdpi, and -xhdpi folders, as described earlier in this chapter. The resource identifier for a Drawable resource is the lowercase file name without its extension.

2.1

The preferred format for a bitmap resource is PNG, although JPG and GIF files are also supported.

Layouts

Layout resources enable you to decouple your presentation layer from your business logic by designing UI layouts in XML rather than constructing them in code.

You can use layouts to define the UI for any visual component, including Activities, Fragments, and Widgets. Once defined in XML, the layout must be “inflated” into the user interface. Within an Activity this is done using setContentView (usually within the onCreate method), whereas Fragment Views are inflated using the inflate method from the Inflator object passed in to the Fragment's onCreateView handler.

For more detailed information on using and creating layouts in Activities and Fragments, see Chapter 4, “Building User Interfaces.”

Using layouts to construct your screens in XML is best practice in Android. The decoupling of the layout from the code enables you to create optimized layouts for different hardware configurations, such as varying screen sizes, orientation, or the presence of keyboards and touchscreens.

Each layout definition is stored in a separate file, each containing a single layout, in the res/layout folder. The filename then becomes the resource identifier.

A thorough explanation of layout containers and View elements is included in the next chapter, but as an example Listing 3.2 shows the layout created by the New Project Wizard. It uses a Linear Layout (described in more detail in Chapter 4) as a layout container for a Text View that displays the “Hello World” greeting.

2.11

Listing 3.2: Hello World layout

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:orientation="vertical"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent">
  <TextView 
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:text="@string/hello"
  />
</LinearLayout>

code snippet PA4AD_Ch03_Manifest_and_Resources/res/layout/main.xml

Animations

Android supports three types of animation:

· Property animations—A tweened animation that can be used to potentially animate any property on the target object by applying incremental changes between two values. This can be used for anything from changing the color or opacity of a View to gradually fade it in or out, to changing a font size, or increasing a character's hit points.

· View animations—Tweened animations that can be applied to rotate, move, and stretch a View.

· Frame animations—Frame-by-frame “cell” animations used to display a sequence of Drawable images.

2.1

A comprehensive overview of creating, using, and applying animations can be found in Chapter 11, “Advanced User Experience.”

Defining animations as external resources enables you to reuse the same sequence in multiple places and provides you with the opportunity to present different animations based on device hardware or orientation.

Property Animations

Property animators were introduced in Android 3.0 (API level 11). It is a powerful framework that can be used to animate almost anything.

Each property animation is stored in a separate XML file in the project's res/animator folder. As with layouts and Drawable resources, the animation's filename is used as its resource identifier.

You can use a property animator to animate almost any property on a target object. You can define animators that are tied to a specific property, or a generic value animator that can be allocated to any property and object.

Property animators are extremely useful and are used extensively for animating Fragments in Android. You will explore them in more detail in Chapter 11.

The following simple XML snippet shows a property animator that changes the opacity of the target object by calling its setAlpha method incrementally between 0 and 1 over the course of a second:

<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
   android:propertyName="alpha"
   android:duration="1000"
   android:valueFrom="0.0"
   android:valueTo="1.0"
/>

View Animations

Each view animation is stored in a separate XML file in the project's res/anim folder. As with layouts and Drawable resources, the animation's filename is used as its resource identifier.

An animation can be defined for changes in alpha (fading), scale (scaling), translate (movement), or rotate (rotation).

Table 3.1 shows the valid attributes, and attribute values, supported by each animation type.

Table 3.1: Animation type attributes

Animation Type

Attributes

Valid Values

Alpha

fromAlpha/toAlpha

Float from 0 to 1

Scale

fromXScale/toXScale

Float from 0 to 1

fromYScale/toYScale

Float from 0 to 1

pivotX/pivotY

String of the percentage of graphic width/height from 0% to 100%

Translate

fromX/toX

Float from 0 to 1

fromY/toY

Float from 0 to 1

Rotate

fromDegrees/toDegrees

Float from 0 to 360

pivotX/pivotY

String of the percentage of graphic width/height from 0% to 100%

You can create a combination of animations using the set tag. An animation set contains one or more animation transformations and supports various additional tags and attributes to customize when and how each animation within the set is run.

The following list shows some of the set tags available:

· duration—Duration of the full animation in milliseconds.

· startOffset—Millisecond delay before the animation starts.

· fillBeforetrue—Applies the animation transformation before it begins.

· fillAftertrue—Applies the animation transformation after it ends.

· interpolator—Sets how the speed of this effect varies over time. Chapter 11 explores the interpolators available. To specify one, reference the system animation resources at android:anim/interpolatorName.

The following example shows an animation set that spins the target 360 degrees while it shrinks and fades out:

2.1

If you do not use the startOffset tag, all the animation effects within a set will execute simultaneously.

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
     android:interpolator="@android:anim/accelerate_interpolator">
  <rotate
    android:fromDegrees="0"
    android:toDegrees="360"
    android:pivotX="50%"
    android:pivotY="50%"
    android:startOffset="500"
    android:duration="1000" />
  <scale
    android:fromXScale="1.0"
    android:toXScale="0.0"
    android:fromYScale="1.0"
    android:toYScale="0.0"
    android:pivotX="50%"
    android:pivotY="50%"
    android:startOffset="500"
    android:duration="500" />
  <alpha
    android:fromAlpha="1.0"
    android:toAlpha="0.0"
    android:startOffset="500"
    android:duration="500" />
</set>

Frame-by-Frame Animations

Frame-by-frame animations produce a sequence of Drawables, each of which is displayed for a specified duration.

Because frame-by-frame animations represent animated Drawables, they are stored in the res/drawable folder and use their filenames (without the .xml extension) as their resource Ids.

The following XML snippet shows a simple animation that cycles through a series of bitmap resources, displaying each one for half a second. To use this snippet, you need to create new image resources android1 through android3:

<animation-list
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:oneshot="false">
  <item android:drawable="@drawable/android1" android:duration="500" />
  <item android:drawable="@drawable/android2" android:duration="500" />
  <item android:drawable="@drawable/android3" android:duration="500" />
</animation-list>

Note that in many cases you should include multiple resolutions of each of the drawables used within the animation list in the drawable-ldpi, -mdi, -hdpi, and -xhdpi folders, as appropriate.

To play the animation, start by assigning the resource to a host View before getting a reference to the Animation Drawable object and starting it:

ImageView androidIV = (ImageView)findViewById(R.id.iv_android);
androidIV.setBackgroundResource(R.drawable.android_anim);
 
AnimationDrawable androidAnimation = 
  (AnimationDrawable) androidIV.getBackground();
 
androidAnimation.start();

Typically, this is done in two steps; assigning the resource to the background should be done within the onCreate handler.

Within this handler the animation is not fully attached to the window, so the animations can't be started; instead, this is usually done as a result to user action (such as a button press) or within the onWindowFocusChanged handler.

Menus

Create menu resources to design your menu layouts in XML, rather than constructing them in code.

You can use menu resources to define both Activity and context menus within your applications, and provide the same options you would have when constructing your menus in code. When defined in XML, a menu is inflated within your application via the inflate method of the MenuInflatorService, usually within the onCreateOptionsMenu method. You examine menus in more detail in Chapter 10.

Each menu definition is stored in a separate file, each containing a single menu, in the res/menu folder—the filename then becomes the resource identifier. Using XML to define your menus is best-practice design in Android.

A thorough explanation of menu options is included in Chapter 10, but Listing 3.3 shows a simple example.

2.11

Listing 3.3: Simple menu layout resource

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
  <item android:id="@+id/menu_refresh"
        android:title="@string/refresh_mi" />
  <item android:id="@+id/menu_settings"
        android:title="@string/settings_mi" />
</menu>

code snippet PA4AD_Snippets_Chapter3/res/menu/menu.xml

Using Resources

In addition to the resources you supply, the Android platform includes several system resources that you can use in your applications. All resources can be used directly from your application code and can also be referenced from within other resources. For example, a dimension resource might be referenced in a layout definition.

Later in this chapter you learn how to define alternative resource values for different languages, locations, and hardware. It's important to note that when using resources, you shouldn't choose a particular specialized version. Android will automatically select the most appropriate value for a given resource identifier based on the current hardware, device, and language configurations.

Using Resources in Code

Access resources in code using the static R class. R is a generated class based on your external resources, and created when your project is compiled. The R class contains static subclasses for each of the resource types for which you've defined at least one resource. For example, the default new project includes the R.string and R.drawable subclasses.

2.1

If you use the ADT plug-in in Eclipse, the R class will be created automatically when you make any change to an external resource file or folder. If you are not using the plug-in, use the AAPT tool to compile your project and generate the R class. R is a compiler-generated class, so don't make any manual modifications to it because they will be lost when the file is regenerated.

Each of the subclasses within R exposes its associated resources as variables, with the variable names matching the resource identifiers—for example, R.string.app_name or R.drawable.icon.

The value of these variables is an integer that represents each resource's location in the resource table, not an instance of the resource itself.

Where a constructor or method, such as setContentView, accepts a resource identifier, you can pass in the resource variable, as shown in the following code snippet:

// Inflate a layout resource.
setContentView(R.layout.main);
// Display a transient dialog box that displays the
// error message string resource.
Toast.makeText(this, R.string.app_error, Toast.LENGTH_LONG).show();

When you need an instance of the resource itself, you need to use helper methods to extract them from the resource table. The resource table is represented within your application as an instance of the Resources class.

These methods perform lookups on the application's current resource table, so these helper methods can't be static. Use the getResources method on your application context, as shown in the following snippet, to access your application's Resources instance:

Resources myResources = getResources();

The Resources class includes getters for each of the available resource types and generally works by passing in the resource ID you'd like an instance of. The following code snippet shows an example of using the helper methods to return a selection of resource values:

Resources myResources = getResources();
 
CharSequence styledText = myResources.getText(R.string.stop_message);
Drawable icon = myResources.getDrawable(R.drawable.app_icon);
 
int opaqueBlue = myResources.getColor(R.color.opaque_blue);
 
float borderWidth = myResources.getDimension(R.dimen.standard_border);
 
Animation tranOut;
tranOut = AnimationUtils.loadAnimation(this, R.anim.spin_shrink_fade);
 
ObjectAnimator animator = 
  (ObjectAnimator)AnimatorInflater.loadAnimator(this,
  R.anim.my_animator);
 
String[] stringArray;
stringArray = myResources.getStringArray(R.array.string_array);
 
int[] intArray = myResources.getIntArray(R.array.integer_array);

Frame-by-frame animated resources are inflated into AnimationResources. You can return the value using getDrawable and casting the return value, as shown here:

AnimationDrawable androidAnimation;
androidAnimation = 
  (AnimationDrawable)myResources.getDrawable(R.drawable.frame_by_frame);

Referencing Resources Within Resources

You can also use resource references as attribute values in other XML resources.

This is particularly useful for layouts and styles, letting you create specialized variations on themes and localized strings and image assets. It's also a useful way to support different images and spacing for a layout to ensure that it's optimized for different screen sizes and resolutions.

To reference one resource from another, use the @ notation, as shown in the following snippet:

attribute="@[packagename:]resourcetype/resourceidentifier"

2.1

Android assumes you use a resource from the same package, so you only need to fully qualify the package name if you use a resource from a different package.

Listing 3.4 shows a layout that uses color, dimension, and string resources.

2.11

Listing 3.4: Using resources in a layout

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:orientation="vertical"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:padding="@dimen/standard_border">
  <EditText
    android:id="@+id/myEditText"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="@string/stop_message"
    android:textColor="@color/opaque_blue"
  />
</LinearLayout>

code snippet PA4AD_Ch03_Manifest_and_Resources/res/layout/reslayout.xml

Using System Resources

The Android framework makes many native resources available, providing you with various strings, images, animations, styles, and layouts to use in your applications.

Accessing the system resources in code is similar to using your own resources. The difference is that you use the native Android resource classes available from android.R, rather than the application-specific R class. The following code snippet uses the getString method available in the application context to retrieve an error message available from the system resources:

CharSequence httpError = getString(android.R.string.httpErrorBadUrl);

To access system resources in XML, specify android as the package name, as shown in this XML snippet:

<EditText
  android:id="@+id/myEditText"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:text="@android:string/httpErrorBadUrl"
  android:textColor="@android:color/darker_gray"
/>

Referring to Styles in the Current Theme

Using themes is an excellent way to ensure consistency for your application's UI. Rather than fully define each style, Android provides a shortcut to enable you to use styles from the currently applied theme.

To do this, use ?android: rather than @ as a prefix to the resource you want to use. The following example shows a snippet of the preceding code but uses the current theme's text color rather than a system resource:

<EditText
  android:id="@+id/myEditText"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:text="@android:string/httpErrorBadUrl"
  android:textColor="?android:textColor"
/>

This technique enables you to create styles that change if the current theme changes, without you modifying each individual style resource.

Creating Resources for Different Languages and Hardware

Using the directory structure described here, you can create different resource values for specific languages, locations, and hardware configurations. Android chooses from among these values dynamically at run time using its dynamic resource-selection mechanism.

You can specify alternative resource values using a parallel directory structure within the res folder. A hyphen (-) is used to separate qualifiers that specify the conditions you provide alternatives for.

The following example hierarchy shows a folder structure that features default string values, with French language and French Canadian location variations:

Project/
  res/
    values/
      strings.xml
    values-fr/
      strings.xml
    values-fr-rCA/
      strings.xml

The following list gives the qualifiers you can use to customize your resource values:

· Mobile Country Code and Mobile Network Code (MCC/MNC)—The country, and optionally the network, associated with the SIM currently used in the device. The MCC is specified by mcc followed by the three-digit country code. You can optionally add the MNC using mnc and the two- or three-digit network code (for example, mcc234-mnc20 or mcc310). You can find a list of MCC/MNC codes on Wikipedia at http://en.wikipedia.org/wiki/MobileNetworkCode.

· Language and Region—Language specified by the lowercase two-letter ISO 639-1 language code, followed optionally by a region specified by a lowercase r followed by the uppercase two-letter ISO 3166-1-alpha-2 language code (for example, en, en-rUS, or en-rGB).

· Smallest Screen Width—The lowest of the device's screen dimensions (height and width) specified in the form sw<Dimension value>dp (for example, sw600dp, sw320dp, or sw720dp). This is generally used when providing multiple layouts, where the value specified should be the smallest screen width that your layout requires in order to render correctly. Where you supply multiple directories with different smallest screen width qualifiers, Android selects the largest value that doesn't exceed the smallest dimension available on the device.

· Available Screen Width—The minimum screen width required to use the contained resources, specified in the form w<Dimension value>dp (for example, w600dp, w320dp, or w720dp). Also used to supply multiple layouts alternatives, but unlike smallest screen width, the available screen width changes to reflect the current screen width when the device orientation changes. Android selects the largest value that doesn't exceed the currently available screen width.

· Available Screen Height—The minimum screen height required to use the contained resources, specified in the form h<Dimension value>dp (for example, h720dp, h480dp, or h1280dp). Like available screen width, the available screen height changes when the device orientation changes to reflect the current screen height. Android selects the largest value that doesn't exceed the currently available screen height.

· Screen Size—One of small (smaller than HVGA), medium (at least HVGA and typically smaller than VGA), large (VGA or larger), or xlarge (significantly larger than HVGA). Because each of these screen categories can include devices with significantly different screen sizes (particularly tablets), it's good practice to use the more specific smallest screen size, and available screen width and height whenever possible. Because they precede this screen size qualifier, where both are specified, the more specific qualifiers will be used in preference where supported.

· Screen Aspect Ratio—Specify long or notlong for resources designed specifically for wide screen. (For example, WVGA is long; QVGA is notlong.)

· Screen Orientation: One of port (portrait), land (landscape), or square (square).

· Dock Mode—One of car or desk. Introduced in API level 8.

· Night Mode—One of night (night mode) or notnight (day mode). Introduced in API level 8. Used in combination with the dock mode qualifier, this provides a simple way to change the theme and/or color scheme of an application to make it more suitable for use at night in a car dock.

· Screen Pixel Density—Pixel density in dots per inch (dpi). Best practice is to supply ldpi, mdpi, hdpi, or xhdpi to specify low (120 dpi), medium (160 dpi), high (240 dpi), or extra high (320 dpi) pixel density assets, respectively. You can specify nodpi for bitmap resources you don't want scaled to support an exact screen density. To better support applications targeting televisions running Android, you can also use the tvdpi qualifier for assets of approximately 213dpi. This is generally unnecessary for most applications, where including medium- and high-resolution assets is sufficient for a good user experience. Unlike with other resource types, Android does not require an exact match to select a resource. When selecting the appropriate folder, it chooses the nearest match to the device's pixel density and scales the resulting Drawables accordingly.

· Touchscreen Type—One of notouch, stylus, or finger, allowing you to provide layouts or dimensions optimized for the style of touchscreen input available on the host device.

· Keyboard Availability—One of keysexposed, keyshidden, or keyssoft.

· Keyboard Input Type—One of nokeys, qwerty, or 12key.

· Navigation Key Availability—One of navexposed or navhidden.

· UI Navigation Type—One of nonav, dpad, trackball, or wheel.

· Platform Version—The target API level, specified in the form v<API Level> (for example, v7). Used for resources restricted to devices running at the specified API level or higher.

You can specify multiple qualifiers for any resource type, separating each qualifier with a hyphen. Any combination is supported; however, they must be used in the order given in the preceding list, and no more than one value can be used per qualifier.

The following example shows valid and invalid directory names for alternative layout resources.

Valid
    layout-large-land
    layout-xlarge-port-keyshidden
    layout-long-land-notouch-nokeys
Invalid
    values-rUS-en (out of order)
    values-rUS-rUK (multiple values for a single qualifier)

When Android retrieves a resource at run time, it finds the best match from the available alternatives. Starting with a list of all the folders in which the required value exists, it selects the one with the greatest number of matching qualifiers. If two folders are an equal match, the tiebreaker is based on the order of the matched qualifiers in the preceding list.

2.1

If no resource matches are found on a given device, your application throws an exception when attempting to access that resource. To avoid this, you should always include default values for each resource type in a folder that includes no qualifiers.

Runtime Configuration Changes

Android handles runtime changes to the language, location, and hardware by terminating and restarting the active Activity. This forces the resource resolution for the Activity to be reevaluated and the most appropriate resource values for the new configuration to be selected.

In some special cases this default behavior may be inconvenient, particularly for applications that don't want to present a different UI based on screen orientation changes. You can customize your application's response to such changes by detecting and reacting to them yourself.

To have an Activity listen for runtime configuration changes, add an android:configChanges attribute to its manifest node, specifying the configuration changes you want to handle.

The following list describes some of the configuration changes you can specify:

· mcc and mnc—A SIM has been detected and the mobile country or network code (respectively) has changed.

· locale—The user has changed the device's language settings.

· keyboardHidden—The keyboard, d-pad, or other input mechanism has been exposed or hidden.

· keyboard—The type of keyboard has changed; for example, the phone may have a 12-key keypad that flips out to reveal a full keyboard, or an external keyboard might have been plugged in.

· fontScale—The user has changed the preferred font size.

· uiMode—The global UI mode has changed. This typically occurs if you switch between car mode, day or night mode, and so on.

· orientation—The screen has been rotated between portrait and landscape.

· screenLayout—The screen layout has changed; typically occurs if a different screen has been activated.

· screenSize—Introduced in Honeycomb MR2 (API level 12), occurs when the available screen size has changed, for example a change in orientation between landscape and portrait.

· smallestScreenSize—Introduced in Honeycomb MR2 (API level 12), occurs when the physical screen size has changed, such as when a device has been connected to an external display.

In certain circumstances multiple events will be triggered simultaneously. For example, when the user slides out a keyboard, most devices fire both the keyboardHidden and orientation events, and connecting an external display on a post-Honeycomb MR2 device is likely to trigger orientation,screenLayout, screenSize, and smallestScreenSize events.

You can select multiple events you want to handle yourself by separating the values with a pipe (|), as shown in Listing 3.5, which shows an activity node declaring that it will handle changes in screen size and orientation, and keyboard visibility.

2.11

Listing 3.5: Activity definition for handling dynamic resource changes

<activity 
  android:name=".MyActivity"
  android:label="@string/app_name"
  android:configChanges="screenSize|orientation|keyboardHidden">
  <intent-filter >
    <action android:name="android.intent.action.MAIN" />
    <category android:name="android.intent.category.LAUNCHER" />
  </intent-filter>
</activity>

code snippet PA4AD_Ch03_Config_Changes/AndroidManifest.xml

Adding an android:configChanges attribute suppresses the restart for the specified configuration changes, instead triggering the onConfigurationChanged handler in the associated Activity. Override this method to handle the configuration changes yourself, using the passed-in Configurationobject to determine the new configuration values, as shown in Listing 3.6. Be sure to call back to the superclass and reload any resource values that the Activity uses, in case they've changed.

2.11

Listing 3.6: Handling configuration changes in code

@Override
public void onConfigurationChanged(Configuration newConfig) {
  super.onConfigurationChanged(newConfig); 
 
  // [ ... Update any UI based on resource values ... ]
 
  if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
    // [ ... React to different orientation ... ]
  }
 
  if (newConfig.keyboardHidden == Configuration.KEYBOARDHIDDEN_NO) {
    // [ ... React to changed keyboard visibility ... ]
  }
}

code snippet PA4AD_Ch03_Config_Changes/src/MyActivity.java

When onConfigurationChanged is called, the Activity's Resource variables have already been updated with the new values, so they'll be safe to use.

Any configuration change that you don't explicitly flag as being handled by your application will cause your Activity to restart, without a call to onConfigurationChanged.

The Android Application Lifecycle

Unlike many traditional application platforms, Android applications have limited control over their own lifecycles. Instead, application components must listen for changes in the application state and react accordingly, taking particular care to be prepared for untimely termination.

By default, each Android application runs in its own process, each of which is running a separate instance of Dalvik. Memory and process management is handled exclusively by the run time.

2.1

You can force application components within the same application to run in different processes or to have multiple applications share the same process using the android:process attribute on the affected component nodes within the manifest.

Android aggressively manages its resources, doing whatever's necessary to ensure a smooth and stable user experience. In practice that means that processes (and their hosted applications) will be killed, in some case without warning, to free resources for higher-priority applications.

Understanding an Application's Priority and Its Process' States

The order in which processes are killed to reclaim resources is determined by the priority of their hosted applications. An application's priority is equal to that of its highest-priority component.

If two applications have the same priority, the process that has been at that priority longest will be killed first. Process priority is also affected by interprocess dependencies; if an application has a dependency on a Service or Content Provider supplied by a second application, the secondary application has at least as high a priority as the application it supports.

2.1

All Android applications continue running and in memory until the system needs resources for other applications.

Figure 3.4 shows the priority tree used to determine the order of application termination.

Figure 3.4

3.4

It's important to structure your application to ensure that its priority is appropriate for the work it's doing. If you don't, your application could be killed while it's in the middle of something important, or it could remain running when it is no longer needed.

The following list details each of the application states shown in Figure 3.4, explaining how the state is determined by the application components of which it comprises:

· Active processes—Active (foreground) processes have application components the user is interacting with. These are the processes Android tries to keep responsive by reclaiming resources from other applications. There are generally very few of these processes, and they will be killed only as a last resort.

· Active processes include the following:

· Activities in an active state—that is, those in the foreground responding to user events. You will explore Activity states in greater detail later in this chapter.

· Broadcast Receivers executing onReceive event handlers as described in Chapter 5.

· Services executing onStart, onCreate, or onDestroy event handlers as described in Chapter 9.

· Running Services that have been flagged to run in the foreground (also described in Chapter 9.)

· Visible processes—Visible but inactive processes are those hosting “visible” Activities. As the name suggests, visible Activities are visible, but they aren't in the foreground or responding to user events. This happens when an Activity is only partially obscured (by a non-full-screen or transparent Activity). There are generally very few visible processes, and they'll be killed only under extreme circumstances to allow active processes to continue.

· Started Service processes—Processes hosting Services that have been started. Because these Services don't interact directly with the user, they receive a slightly lower priority than visible Activities or foreground Services. Applications with running Services are still considered foreground processes and won't be killed unless resources are needed for active or visible processes. When the system terminates a running Service it will attempt to restart them (unless you specify that it shouldn't) when resources become available. You'll learn more about Services in Chapter 9.

· Background processes—Processes hosting Activities that aren't visible and that don't have any running Services. There will generally be a large number of background processes that Android will kill using a last-seen-first-killed pattern in order to obtain resources for foreground processes.

· Empty processes—To improve overall system performance, Android will often retain an application in memory after it has reached the end of its lifetime. Android maintains this cache to improve the start-up time of applications when they're relaunched. These processes are routinely killed, as required.

Introducing the Android Application Class

Your application's Application object remains instantiated whenever your application runs. Unlike Activities, the Application is not restarted as a result of configuration changes. Extending the Application class with your own implementation enables you to do three things:

· Respond to application level events broadcast by the Android run time such as low memory conditions.

· Transfer objects between application components.

· Manage and maintain resources used by several application components.

Of these, the latter two can be better achieved using a separate singleton class. When your Application implementation is registered in the manifest, it will be instantiated when your application process is created. As a result, your Application implementation is by nature a singleton and should be implemented as such to provide access to its methods and member variables.

Extending and Using the Application Class

Listing 3.7 shows the skeleton code for extending the Application class and implementing it as a singleton.

2.11

Listing 3.7: Skeleton Application class

import android.app.Application;
import android.content.res.Configuration;
 
public class MyApplication extends Application {
 
  private static MyApplication singleton;
 
  // Returns the application instance
  public static MyApplication getInstance() {
    return singleton;
  }
 
  @Override
  public final void onCreate() {
    super.onCreate();
    singleton = this;
  }
}

code snippet PA4AD_Ch03_Config_Changes/src/MyApplication.java

When created, you must register your new Application class in the manifest's application node using a name attribute, as shown in the following snippet:

<application android:icon="@drawable/icon"
             android:name=".MyApplication">
  [... Manifest nodes ...]
</application>

Your Application implementation will be instantiated when your application is started. Create new state variables and global resources for access from within the application components:

MyObject value = MyApplication.getInstance().getGlobalStateValue();
MyApplication.getInstance().setGlobalStateValue(myObjectValue);

Although this can be an effective technique for transferring objects between your loosely coupled application components, or for maintaining application state or shared resources, it is often better to create your own static singleton class rather than extending the Application class specifically unless you are also handling the lifecycle events described in the following section.

Overriding the Application Lifecycle Events

The Application class provides event handlers for application creation and termination, low memory conditions, and configuration changes (as described in the previous section).

By overriding these methods, you can implement your own application-specific behavior for each of these circumstances:

· onCreate—Called when the application is created. Override this method to initialize your application singleton and create and initialize any application state variables or shared resources.

· onLowMemory—Provides an opportunity for well-behaved applications to free additional memory when the system is running low on resources. This will generally only be called when background processes have already been terminated and the current foreground applications are still low on memory. Override this handler to clear caches or release unnecessary resources.

· onTrimMemory—An application specific alternative to the onLowMemory handler introduced in Android 4.0 (API level 13). Called when the run time determines that the current application should attempt to trim its memory overhead – typically when it moves to the background. It includes a level parameter that provides the context around the request.

· onConfigurationChanged—Unlike Activities Application objects are not restarted due to configuration changes. If your application uses values dependent on specific configurations, override this handler to reload those values and otherwise handle configuration changes at an application level.

As shown in Listing 3.8, you must always call through to the superclass event handlers when overriding these methods.

2.11

Listing 3.8: Overriding the Application Lifecycle Handlers

public class MyApplication extends Application {
 
  private static MyApplication singleton;
 
  // Returns the application instance
  public static MyApplication getInstance() {
    return singleton;
  }
 
  @Override
  public final void onCreate() {
    super.onCreate();
    singleton = this;
  }
 
  @Override
  public final void onLowMemory() {
    super.onLowMemory();
  }
 
  @Override
  public final void onTrimMemory(int level) {
    super.onTrimMemory(level);
  }
 
  @Override
  public final void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);
  }
}

code snippet PA4AD_Snippets_Chapter3/MyApplication.java

A Closer Look at Android Activities

Each Activity represents a screen that an application can present to its users. The more complicated your application, the more screens you are likely to need.

Typically, this includes at least a primary interface screen that handles the main UI functionality of your application. This primary interface generally consists of a number of Fragments that make up your UI and is generally supported by a set of secondary Activities. To move between screens you start a new Activity (or return from one).

Most Activities are designed to occupy the entire display, but you can also create semitransparent or floating Activities.

Creating Activities

Extend Activity to create a new Activity class. Within this new class you must define the UI and implement your functionality. Listing 3.9 shows the basic skeleton code for a new Activity.

2.11

Listing 3.9: Activity skeleton code

package com.paad.activities;
 
import android.app.Activity;
import android.os.Bundle;
 
public class MyActivity extends Activity {
 
  /** Called when the activity is first created. */
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
  }
}

code snippet PA4AD_Ch03_Activities/src/MyActivity.java

The base Activity class presents an empty screen that encapsulates the window display handling. An empty Activity isn't particularly useful, so the first thing you'll want to do is create the UI with Fragments, layouts, and Views.

Views are the UI controls that display data and provide user interaction. Android provides several layout classes, called View Groups, which can contain multiple Views to help you layout your UIs. Fragments are used to encapsulate segments of your UI, making it simple to create dynamic interfaces that can be rearranged to optimize your layouts for different screen sizes and orientations.

2.1

Chapter 4 discusses Views, View Groups, layouts, and Fragments in detail, examining what's available, how to use them, and how to create your own.

To assign a UI to an Activity, call setContentView from the onCreate method of your Activity.

In this first snippet, an instance of a TextView is used as the Activity's UI:

@Override
public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  TextView textView = new TextView(this);
  setContentView(textView);
}

Usually, you'll want to use a more complex UI design. You can create a layout in code using layout View Groups, or you can use the standard Android convention of passing a resource ID for a layout defined in an external resource, as shown in the following snippet:

@Override
public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.main);
}

To use an Activity in your application, you need to register it in the manifest. Add a new activity tag within the application node of the manifest; the activity tag includes attributes for metadata, such as the label, icon, required permissions, and themes used by the Activity. An Activitywithout a corresponding activity tag can't be displayed—attempting to do so will result in a runtime exception.

<activity android:label="@string/app_name"
          android:name=".MyActivity">
</activity>

Within the activity tag you can add intent-filter nodes that specify the Intents that can be used to start your Activity. Each Intent Filter defines one or more actions and categories that your Activity supports. Intents and Intent Filters are covered in depth in Chapter 5, but it's worth noting that for an Activity to be available from the application launcher, it must include an Intent Filter listening for the MAIN action and the LAUNCHER category, as highlighted in Listing 3.10.

2.11

Listing 3.10: Main Application Activity Definition

<activity android:label="@string/app_name"
          android:name=".MyActivity">
  <intent-filter>
    <action android:name="android.intent.action.MAIN" />
    <category android:name="android.intent.category.LAUNCHER" />
  </intent-filter>
</activity>

code snippet PA4AD_Ch03_Activities/AndroidManifest.xml

The Activity Lifecycle

A good understanding of the Activity lifecycle is vital to ensure that your application provides a seamless user experience and properly manages its resources.

As explained earlier, Android applications do not control their own process lifetimes; the Android run time manages the process of each application, and by extension that of each Activity within it.

Although the run time handles the termination and management of an Activity's process, the Activity's state helps determine the priority of its parent application. The application priority, in turn, influences the likelihood that the run time will terminate it and the Activities running within it.

Activity Stacks

The state of each Activity is determined by its position on the Activity stack, a last-in–first-out collection of all the currently running Activities. When a new Activity starts, it becomes active and is moved to the top of the stack. If the user navigates back using the Back button, or the foreground Activity is otherwise closed, the next Activity down on the stack moves up and becomes active. Figure 3.5 illustrates this process.

Figure 3.5

3.5

As described previously in this chapter, an application's priority is influenced by its highest-priority Activity. When the Android memory manager is deciding which application to terminate to free resources, it uses this Activity stack to determine the priority of applications.

Activity States

As Activities are created and destroyed, they move in and out of the stack, as shown in Figure 3.5. As they do so, they transition through four possible states:

· Active—When an Activity is at the top of the stack it is the visible, focused, foreground Activity that is receiving user input. Android will attempt to keep it alive at all costs, killing Activities further down the stack as needed, to ensure that it has the resources it needs. When another Activity becomes active, this one will be paused.

· Paused—In some cases your Activity will be visible but will not have focus; at this point it's paused. This state is reached if a transparent or non-full-screen Activity is active in front of it. When paused, an Activity is treated as if it were active; however, it doesn't receive user input events. In extreme cases Android will kill a paused Activity to recover resources for the active Activity. When an Activity becomes totally obscured, it is stopped.

· Stopped—When an Activity isn't visible, it “stops.” The Activity will remain in memory, retaining all state information; however, it is now a candidate for termination when the system requires memory elsewhere. When an Activity is in a stopped state, it's important to save data and the current UI state, and to stop any non-critical operations. Once an Activity has exited or closed, it becomes inactive.

· Inactive—After an Activity has been killed, and before it's been launched, it's inactive. Inactive Activities have been removed from the Activity stack and need to be restarted before they can be displayed and used.

State transitions are nondeterministic and are handled entirely by the Android memory manager. Android will start by closing applications that contain inactive Activities, followed by those that are stopped. In extreme cases, it will remove those that are paused.

2.1

To ensure a seamless user experience, transitions between states should be invisible to the user. There should be no difference in an Activity moving from a paused, stopped, or inactive state back to active, so it's important to save all UI state and persist all data when an Activity is paused or stopped. Once an Activity does become active, it should restore those saved values.

Similarly, apart from changes to the Activity's priority, transitions between the active, paused, and stopped states have little direct impact on the Activity itself. It's up to you to use these signals to pause and stop your Activities accordingly

Monitoring State Changes

To ensure that Activities can react to state changes, Android provides a series of event handlers that are fired when an Activity transitions through its full, visible, and active lifetimes. Figure 3.6 summarizes these lifetimes in terms of the Activity states described in the previous section.

Figure 3.6

3.6

The skeleton code in Listing 3.11 shows the stubs for the state change method handlers available in an Activity. Comments within each stub describe the actions you should consider taking on each state change event.

2.11

Listing 3.11: Activity state event handlers

package com.paad.activities;
import android.app.Activity;
import android.os.Bundle;
 
public class MyStateChangeActivity extends Activity {
 
  // Called at the start of the full lifetime.
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // Initialize Activity and inflate the UI.
  }
 
  // Called after onCreate has finished, use to restore UI state
  @Override
  public void onRestoreInstanceState(Bundle savedInstanceState) {
    super.onRestoreInstanceState(savedInstanceState);
    // Restore UI state from the savedInstanceState.
    // This bundle has also been passed to onCreate.
    // Will only be called if the Activity has been 
    // killed by the system since it was last visible.
  }
 
  // Called before subsequent visible lifetimes
  // for an Activity process.
  @Override
  public void onRestart(){
    super.onRestart();
    // Load changes knowing that the Activity has already
    // been visible within this process.
  }
 
  // Called at the start of the visible lifetime.
  @Override
  public void onStart(){
    super.onStart();
    // Apply any required UI change now that the Activity is visible.
  }
 
  // Called at the start of the active lifetime.
  @Override
  public void onResume(){
    super.onResume();
    // Resume any paused UI updates, threads, or processes required
    // by the Activity but suspended when it was inactive.
  }
 
  // Called to save UI state changes at the
  // end of the active lifecycle.
  @Override
  public void onSaveInstanceState(Bundle savedInstanceState) {
    // Save UI state changes to the savedInstanceState.
    // This bundle will be passed to onCreate and 
    // onRestoreInstanceState if the process is
    // killed and restarted by the run time.
    super.onSaveInstanceState(savedInstanceState);
  }
 
  // Called at the end of the active lifetime.
  @Override
  public void onPause(){
    // Suspend UI updates, threads, or CPU intensive processes
    // that don't need to be updated when the Activity isn't
    // the active foreground Activity.
    super.onPause();
  }
 
  // Called at the end of the visible lifetime.
  @Override
  public void onStop(){
    // Suspend remaining UI updates, threads, or processing
    // that aren't required when the Activity isn't visible.
    // Persist all edits or state changes
    // as after this call the process is likely to be killed.
    super.onStop();
  }
 
  // Sometimes called at the end of the full lifetime.
  @Override
  public void onDestroy(){
    // Clean up any resources including ending threads,
    // closing database connections etc.
    super.onDestroy();
  }
}

code snippet PA4AD_Ch03_Activities/src/MyStateChangeActivity.java

As shown in the preceding code, you should always call back to the superclass when overriding these event handlers.

Understanding Activity Lifetimes

Within an Activity's full lifetime, between creation and destruction, it goes through one or more iterations of the active and visible lifetimes. Each transition triggers the method handlers previously described. The following sections provide a closer look at each of these lifetimes and the events that bracket them.

The Full Lifetime

The full lifetime of your Activity occurs between the first call to onCreate and the final call to onDestroy. It's not uncommon for an Activity's process to be terminated without the onDestroy method being called.

Use the onCreate method to initialize your Activity: inflate the user interface, get references to Fragments, allocate references to class variables, bind data to controls, and start Services and Timers. If the Activity was terminated unexpectedly by the runtime, the onCreate method is passed aBundle object containing the state saved in the last call to onSaveInstanceState. You should use this Bundle to restore the UI to its previous state, either within the onCreate method or onRestoreInstanceState.

Override onDestroy to clean up any resources created in onCreate, and ensure that all external connections, such as network or database links, are closed.

As part of Android's guidelines for writing efficient code, it's recommended that you avoid the creation of short-term objects. The rapid creation and destruction of objects force additional garbage collection, a process that can have a direct negative impact on the user experience. If your Activity creates the same set of objects regularly, consider creating them in the onCreate method instead, as it's called only once in the Activity's lifetime.

The Visible Lifetime

An Activity's visible lifetimes are bound between calls to onStart and onStop. Between these calls your Activity will be visible to the user, although it may not have focus and may be partially obscured. Activities are likely to go through several visible lifetimes during their full lifetime because they move between the foreground and background. Although it's unusual, in extreme cases the Android run time will kill an Activity during its visible lifetime without a call to onStop.

The onStop method should be used to pause or stop animations, threads, Sensor listeners, GPS lookups, Timers, Services, or other processes that are used exclusively to update the UI. There's little value in consuming resources (such as CPU cycles or network bandwidth) to update the UI when it isn't visible. Use the onStart (or onRestart) methods to resume or restart these processes when the UI is visible again.

The onRestart method is called immediately prior to all but the first call to onStart. Use it to implement special processing that you want done only when the Activity restarts within its full lifetime.

The onStart/onStop methods are also used to register and unregister Broadcast Receivers used exclusively to update the UI.

2.1

You'll learn more about using Broadcast Receivers in Chapter 5.

The Active Lifetime

The active lifetime starts with a call to onResume and ends with a corresponding call to onPause.

An active Activity is in the foreground and is receiving user input events. Your Activity is likely to go through many active lifetimes before it's destroyed, as the active lifetime will end when a new Activity is displayed, the device goes to sleep, or the Activity loses focus. Try to keep code in theonPause and onResume methods relatively fast and lightweight to ensure that your application remains responsive when moving in and out of the foreground.

Immediately before onPause, a call is made to onSaveInstanceState. This method provides an opportunity to save the Activity's UI state in a Bundle that may be passed to the onCreate and onRestoreInstanceState methods. Use onSaveInstanceState to save the UI state (such as checkbox states, user focus, and entered but uncommitted user input) to ensure that the Activity can present the same UI when it next becomes active. You can safely assume that during the active lifetime onSaveInstanceState and onPause will be called before the process is terminated.

Most Activity implementations will override at least the onSaveInstanceState method to commit unsaved changes, as it marks the point beyond which an Activity may be killed without warning. Depending on your application architecture you may also choose to suspend threads, processes, or Broadcast Receivers while your Activity is not in the foreground.

The onResume method can be lightweight. You do not need to reload the UI state here because this is handled by the onCreate and onRestoreInstanceState methods when required. Use onResume to reregister any Broadcast Receivers or other processes you may have suspended in onPause.

Android Activity Classes

The Android SDK includes a selection of Activity subclasses that wrap up the use of common UI widgets. Some of the more useful ones are listed here:

· MapActivity—Encapsulates the resource handling required to support a MapView widget within an Activity. Learn more about MapActivity and MapView in Chapter 13.

· ListActivity—Wrapper class for Activities that feature a ListView bound to a data source as the primary UI metaphor, and expose event handlers for list item selection.

· ExpandableListActivity—Similar to the ListActivity but supports an ExpandableListView.