Chapter 1. Getting Started with Android

Android is hot, and many people are developing Android applications (apps for short). Perhaps you would also like to develop apps, but are unsure about how to get started. Although you could study Google's online Android Developer's Guide (http://developer.android.com/guide/index.html) to acquire the needed knowledge, you might be overwhelmed by the vast amount of information that this guide presents. In contrast, this chapter provides just enough theory to help you understand the basics of Android. This theory is followed by several recipes that teach you how to develop apps and prepare them for publication to Google's Android Market.

What Is Android?

The Android Developer's Guide defines Android as a software stack – a set of software subsystems needed to deliver a fully functional solution – for mobile devices. This stack includes an operating system (a modified version of the Linux kernel), middleware (software that connects the low-level operating system to high-level apps) that's partly based on Java, and key apps (written in Java) such as a web browser (known as Browser) and a contact manager (known as Contacts).

Android offers the following features:

  • Application framework enabling reuse and replacement of app components (discussed later in this chapter)

  • Bluetooth, EDGE, 3G, and WiFi support (hardware dependent)

  • Camera, GPS, compass, and accelerometer support (hardware dependent)

  • Dalvik Virtual Machine (DVM) optimized for mobile devices

  • GSM Telephony support (hardware dependent)

  • Integrated browser based on the open source WebKit engine

  • Media support for common audio, video, and still image formats (MPEG4, H.264, MP3, AAC, AMR, JPG, PNG, GIF)

  • Optimized graphics powered by a custom 2D graphics library; 3D graphics based on the OpenGL ES 1.0 specification (hardware acceleration optional)

  • SQLite for structured data storage

Although not part of an Android device's software stack, Android's rich development environment (including a device emulator and a plugin for the Eclipse IDE) could also be considered an Android feature.

History of Android

Contrary to what you might expect, Android did not originate with Google. Instead, Android was initially developed by Android, Inc., a small Palo Alto, California-based startup company. Google bought this company in July 2005 and released a preview version of the Android SDK in November 2007.

In mid-August, 2008, Google released the Android 0.9 SDK beta, and subsequently released the Android 1.0 SDK one month later. Table 1-1 outlines subsequent SDK update releases. (Starting with version 1.5, each major release comes under a code name that's based on a dessert item.)

Table 1.1. Android Update Releases

SDK Update

Release Date and Changes

1.1

Google released SDK 1.1 on February 9, 2009. Changes included paid apps (via Android Market) and "search by voice" support.

1.5 (Cupcake) Based on Linux Kernel 2.6.27

Google released SDK 1.5 on April 30, 2009. Changes included the ability to record and watch videos through camcorder mode, the ability to upload videos to YouTube and pictures to Picasa, the ability to populate the home screen with widgets, and animated screen transitions.

1.6 (Donut) Based on Linux Kernel 2.6.29

Google released SDK 1.6 on September 15, 2009. Changes included an improved Android Market experience, an integrated camera/camcorder/gallery interface, updated "search by voice" with speed and other improvements, and an updated search experience.

2.0/2.1 (Eclair) Based on Linux Kernel 2.6.29

Google released SDK 2.0 on October 26, 2009. Changes included a revamped user interface, a new contacts list, support for Microsoft Exchange, digital zoom, improved Google Maps (version 3.1.2), HTML5 support for the Browser app, live wallpapers, and Bluetooth 2.1 support.

Google subsequently released SDK update 2.0.1 on December 3, 2009, and SDK update 2.1 on January 12, 2010.

2.2 (Froyo) Based on Linux Kernel 2.6.32

Google released SDK 2.2 on May 20, 2009. Changes included the integration of Chrome's V8 JavaScript engine into the Browser app, voice dialing and contact sharing over Bluetooth, Adobe Flash 10.1 support, additional app speed improvements courtesy of a JIT implementation, and USB tethering and WiFi hotspot functionality.

2.3 (Gingerbread) Based on Linux Kernel 2.6.35.7

Google released SDK 2.3 on December 6, 2010. Changes included a new concurrent garbage collector that improves an app's responsiveness, support for gyroscope sensing, support for WebM video playback and other video improvements, support for near field communication, and improved social networking features. This book focuses on Android 2.3.

Google subsequently released SDK 2.3.1 to fix some bugs, and SDK 2.3.3, a small feature release that adds several improvements and APIs to the Android 2.3 platform.

3.0 (Honeycomb) Based on Linux 2.6.36

Google released SDK 3.0 on February 22, 2011. Unlike previous releases, version 3.0 focuses exclusively on tablets, such as Motorola Zoom, the first tablet to be released (on February 24, 2011). In addition to an improved user interface, version 3.0 improves multitasking, supports multicore processors, supports hardware acceleration, and provides a 3D desktop with redesigned widgets.

Android Architecture

The Android software stack consists of apps at the top, middleware (consisting of an application framework, libraries, and the Android runtime) in the middle, and a Linux kernel with various drivers at the bottom. Figure 1-1 shows this layered architecture.

Android's layered architecture consists of several major parts.

Figure 1.1. Android's layered architecture consists of several major parts.

Users care about apps, and Android ships with a variety of useful core apps, which include Browser, Contacts, and Phone. All apps are written in the Java programming language. Apps form the top layer of Android's architecture.

Directly beneath the app layer is the application framework, a set of high-level building blocks for creating apps. The application framework is preinstalled on Android devices and consists of the following components:

  • Activity Manager: This component provides an app's lifecycle and maintains a shared activity stack for navigating within and among apps. Both topics are discussed later in this chapter.

  • Content Providers: These components encapsulate data (such as the Browser app's bookmarks) that can be shared among apps.

  • Location Manager: This component makes it possible for an Android device to be aware of its physical location.

  • Notification Manager: This component lets an app notify the user of a significant event (such as a message's arrival) without interrupting what the user is currently doing.

  • Package Manager: This component lets an app learn about other app packages that are currently installed on the device. (App packages are discussed later in this chapter.)

  • Resource Manager: This component lets an app access its resources, a topic that's briefly discussed in Recipe 1-5.

  • Telephony Manager: This component lets an app learn about a device's telephony services. It also handles making and receiving phone calls.

  • View System: This component manages user interface elements and user interface-oriented event generation. (These topics are briefly discussed in Recipe 1-5.)

  • Window Manager: This component organizes the screen's real estate into windows, allocates drawing surfaces, and performs other window-related jobs.

The components of the application framework rely on a set of C/C++ libraries to perform their jobs. Developers interact with the following libraries by way of framework APIs:

  • FreeType: This library supports bitmap and vector font rendering.

  • libc: This library is a BSD-derived implementation of the standard C system library, tuned for embedded Linux-based devices.

  • LibWebCore: This library offers a modern and fast web browser engine that powers the Android browser and an embeddable web view. It's based on WebKit (http://en.wikipedia.org/wiki/WebKit) and is also used by the Google Chrome and Apple Safari browsers.

  • Media Framework: These libraries, which are based on PacketVideo's OpenCORE, support the playback and recording of many popular audio and video formats, as well as working with static image files. Supported formats include MPEG4, H.264, MP3, AAC, AMR, JPEG, and PNG.

  • OpenGL | ES: These 3D graphics libraries provide an OpenGL implementation based on OpenGL | ES 1.0 APIs. They use hardware 3D acceleration (where available) or the included (and highly optimized) 3D software rasterizer.

  • SGL: This library provides the underlying 2D graphics engine.

  • SQLite: This library provides a powerful and lightweight relational database engine that's available to all apps, and that's also used by Mozilla Firefox and Apple's iPhone for persistent storage.

  • SSL: This library provides secure sockets layer-based (SSL-based) security for network communication.

  • Surface Manager: This library manages access to the display subsystem, and seamlessly composites 2D and 3D graphic layers from multiple apps.

Android provides a runtime environment that consists of core libraries (implementing a subset of the Apache Harmony Java version 5 implementation) and the Dalvik Virtual Machine (DVM), a non-Java virtual machine that's based on processor registers instead of being stack-based.

Note

Google's Dan Bornstein created Dalvik and named this virtual machine after an Icelandic fishing village where some of his ancestors lived.

Each Android app defaults to running in its own Linux process, which hosts an instance of Dalvik. This virtual machine has been designed so that devices can run multiple virtual machines efficiently. This efficiency is largely due to Dalvik executing Dalvik Executable (DEX)-based files – DEX is a format that's optimized for a minimal memory footprint.

Note

Android starts a process when any of part of the app needs to execute, and shuts down the process when it's no longer needed and system resources are required by other apps.

Perhaps you're wondering how it's possible to have a non-Java virtual machine run Java code. The answer is that Dalvik doesn't run Java code. Instead, Android transforms compiled Java classfiles into the DEX format, and it's this resulting code that gets executed by Dalvik.

Finally, the libraries and Android runtime rely on the Linux kernel (version 2.6) for underlying core services such as threading, low-level memory management, a network stack, process management, and a driver model. Furthermore, the kernel acts as an abstraction layer between the hardware and the rest of the software stack.

App Architecture

The architecture of an Android app differs from desktop application architecture. App architecture is based upon components that communicate with each other by using intents that are described by a manifest and that are stored in an app package.

Components

An app is a collection of components (activities, services, content providers, and broadcast receivers) that run in a Linux process and that are managed by Android. These components share a set of resources, including databases, preferences, a filesystem, and the Linux process.

Note

Not all of these components need to be present in an app. For example, one app might consist of activities only, whereas another app might consist of activities and a service.

This component-oriented architecture lets an app reuse the components of other apps, provided that those other apps permit reuse of their components. Component reuse reduces overall memory footprint, which is very important for devices with limited memory.

To make the reuse concept concrete, suppose you're creating a drawing app that lets users choose a color from a palette, and suppose that another app has developed a suitable color chooser and permits this component to be reused. In this scenario, the drawing app can call upon that other app's color chooser to have the user select a color rather than provide its own color chooser. The drawing app doesn't contain the other app's color chooser or even link to this other app. Instead, it starts up the other app's color chooser component when needed.

Android starts a process when any part of the app (such as the aforementioned color chooser) is needed, and instantiates the Java objects for that part. This is why Android's apps don't have a single entry point (no C-style main() function, for example). Instead, apps use components that are instantiated and run as needed.

Activities

An activity is a component that presents a user interface so that the user can interact with an app. For example, Android's Contacts app includes an activity for entering a new contact, its Phone app includes an activity for dialing a phone number, and its Calculator app includes an activity for performing basic calculations (see Figure 1-2).

The main activity of Android's Calculator app lets the user perform basic calculations.

Figure 1.2. The main activity of Android's Calculator app lets the user perform basic calculations.

Although an app can include a single activity, it's more typical for apps to include multiple activities. For example, Calculator also includes an "advanced panel" activity that lets the user calculate square roots, perform trigonometry, and carry out other advanced mathematical operations.

Services

A service is a component that runs in the background for an indefinite period of time, and which doesn't provide a user interface. As with an activity, a service runs on the process's main thread; it must spawn another thread to perform a time-consuming operation. Services are classified as local or remote.

  • A local service runs in the same process as the rest of the app. Such services make it easy to implement background tasks.

  • A remote service runs in a separate process. Such services let you perform interprocess communications.

Note

A service is not a separate process, although it can be specified to run in a separate process. Also, a service is not a thread. Instead, a service lets the app tell Android about something it wants to be doing in the background (even when the user is not directly interacting with the app), and lets the app expose some of its functionality to other apps.

Consider a service that plays music in response to a user's music choice via an activity. The user selects the song to play via this activity, and a service is started in response to the selection. The service plays the music on another thread to prevent the Application Not Responding dialog box (discussed in Appendix C) from appearing.

Note

The rationale for using a service to play the music is that the user expects the music to keep playing even after the activity that initiated the music leaves the screen.

Broadcast Receivers

A broadcast receiver is a component that receives and reacts to broadcasts. Many broadcasts originate in system code; for example, an announcement is made to indicate that the timezone has been changed or the battery power is low.

Apps can also initiate broadcasts. For example, an app may want to let other apps know that some data has finished downloading from the network to the device and is now available for them to use.

Content Providers

A content provider is a component that makes a specific set of an app's data available to other apps. The data can be stored in the Android filesystem, in an SQLite database, or in any other manner that makes sense.

Content providers are preferable to directly accessing raw data because they decouple component code from raw data formats. This decoupling prevents code breakage when formats change.

Intents

Intents are messages that describe operations to perform (such as "send an email" or "choose a photo"), or in the case of broadcasts, provide descriptions of external events that have occurred (a device's camera being activated, for example) and are being announced.

Because nearly everything in Android involves intents, there are many opportunities to replace existing components with your own components. For example, Android provides the intent for sending an email. Your app can send that intent to activate the standard mail app, or it can register an activity that responds to the "send an email" intent, effectively replacing the standard mail app with its own activity.

These messages are implemented as instances of the android.content.Intent class. An Intent object describes a message in terms of some combination of the following items:

  • Action: A string naming the action to be performed or, in the case of broadcast intents, the action that took place and is being reported. Actions are described by Intent constants such as ACTION_CALL (initiate a phone call), ACTION_EDIT (display data for the user to edit), and ACTION_MAIN (start up as the initial activity). You can also define your own action strings for activating the components in your app. These strings should include the app package as a prefix ("com.example.project.SELECT_COLOR", for example).

  • Category: A string that provides additional information about the kind of component that should handle the intent. For example, CATEGORY_LAUNCHER means that the calling activity should appear in the device's app launcher as a top-level app. (The app launcher is briefly discussed in Recipe 1-4.)

  • Component name: A string that specifies the fully qualified name (package plus name) of a component class to use for the intent. The component name is optional. If set, the Intent object is delivered to an instance of the designated class. If not set, Android uses other information in the Intent object to locate a suitable target.

  • Data: The uniform resource identifier of the data on which to operate (such as a person record in a contacts database).

  • Extras: A set of key-value pairs providing additional information that should be delivered to the component handling the intent. For example, given an action for sending an email, this information could include the message's subject, body, and so on.

  • Flags: Bit values that instruct Android on how to launch an activity (for example, which task the activity should belong to – tasks are discussed later in this chapter) and how to treat the activity after launch (for example, whether the activity can be considered a recent activity). Flags are represented by constants in the Intent class; for example, FLAG_ACTIVITY_NEW_TASK specifies that this activity will become the start of a new task on this activity stack. The activity stack is discussed later in this chapter.

  • Type: The MIME type of the intent data. Normally, Android infers a type from the data. By specifying a type, you disable that inference.

Intents can be classified as explicit or implicit. An explicit intent designates the target component by its name (the previously mentioned component name item is assigned a value). Because component names are usually unknown to the developers of other apps, explicit intents are typically used for app-internal messages (such as an activity that launches another activity located within the same app). Android delivers an explicit intent to an instance of the designated target class. Only the Intent object's component name matters for determining which component should get the intent.

An implicit intent doesn't name a target (the component name is not assigned a value). Implicit intents are often used to start components in other apps. Android searches for the best component (a single activity or service to perform the requested action) or components (a set of broadcast receivers to respond to the broadcast announcement) to handle the implicit intent. During the search, Android compares the contents of the Intent object to intent filters, manifest information associated with components that can potentially receive intents.

Filters advertise a component's capabilities and identify only those intents that the component can handle. They open up the component to the possibility of receiving implicit intents of the advertised type. If a component has no intent filters, it can receive only explicit intents. In contrast, a component with filters can receive explicit and implicit intents. Android consults an Intent object's action, category, data, and type when comparing the intent against an intent filter. It doesn't take extras and flags into consideration.

Manifest

Android learns about an app's various components (and more) by examining the app's XML-structured manifest file, AndroidManifest.xml. For example, Listing 1-1 shows how this file might declare an activity component.

Example 1.1. A Manifest File Declaring an Activity

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.example.project" android:versionCode="1"
          android:versionName="1.0">
   <application android:label="@string/app_name" android:icon="@drawable/icon">
      <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>
   </application>
</manifest>

Listing 1-1 begins with the necessary <?xml version="1.0" encoding="utf-8"?> prolog, which identifies this file as an XML version 1.0 file, whose content is encoded according to the UTF-8 encoding standard.

Listing 1-1 next presents a <manifest> tag, which is this XML document's root element; android identifies the Android namespace, package identifies the app's Java package, and versionCode/versionName identify version information.

Nested within <manifest> is <application>, which is the parent of app component tags. The icon and label attributes refer to icon and label resources that Android devices display to represent the app. (Resources are briefly discussed in Recipe 1-5.)

Note

Resources are identified by the @ prefix, followed by a resource category name (such as string or drawable), /, and the resource ID (such as app_name or icon).

The <application> tag's icon and label attributes specify defaults that are inherited by components whose tags don't specify these attributes.

Nested within <application> is <activity>, which describes an activity component. This tag's name attribute identifies a class (MyActivity) that implements the activity. This name begins with a period character to imply that it's relative to com.example.project.

Note

The period is not present when AndroidManifest.xml is created at the command line. However, this character is present when this file is created from within Eclipse (discussed in Recipe 1-10). Regardless, MyActivity is relative to <manifest>'s package value (com.example.project).

Nested within <activity> is <intent-filter>. This tag declares the capabilities of the component described by the enclosing tag. For example, it declares the capabilities of the activity component via its nested <action> and <category> tags.

  • <action> identifies the action to perform. This tag's android:name attribute is assigned "android.intent.action.MAIN" to identify the activity as the app's entry point.

  • <category> identifies a component category. This tag's android:name attribute is assigned "android.intent.category.LAUNCHER" to identify the activity as needing to be displayed in the app launcher.

Note

Other components are similarly declared. For example, services are declared via <service> tags, broadcast receivers are declared via <receiver> tags, and content providers are declared via <provider> tags. Except for broadcast receivers, which can be created at runtime, components not declared in the manifest are not created by Android.

The manifest may also contain <uses-permission> tags to identify permissions that the app needs. For example, an app that needs to use the camera would specify the following tag: <uses-permission android:name="android.permission.CAMERA" />.

Note

<uses-permission> tags are nested within <manifest> tags. They appear at the same level as the <application> tag.

At app-install time, permissions requested by the app (via <uses-permission>) are granted to it by Android's package installer, based upon checks against the digital signatures of the apps declaring those permissions and/or interaction with the user.

No checks with the user are done while an app is running. It was granted a specific permission when installed and can use that feature as desired, or the permission was not granted and any attempt to use the feature will fail without prompting the user.

Note

AndroidManifest.xml provides additional information, such as naming any libraries that the app needs to be linked against (besides the default Android library), and identifying all app-enforced permissions (via <permission> tags) to other apps, such as controlling who can start the app's activities.

App Package

Android apps are written in Java. The compiled Java code for an app's components is further transformed into Dalvik's DEX format. The resulting code files along with any other required data and resources are subsequently bundled into an App PacKage (APK), a file identified by the .apk suffix.

An APK is not an app, but is used to distribute an app and install it on a mobile device. It's not an app because its components may reuse another APK's components, and (in this situation) not all of the app would reside in a single APK. However, it's common to refer to an APK as representing a single app.

An APK must be signed with a certificate (which identifies the app's author) whose private key is held by its developer. The certificate doesn't need to be signed by a certificate authority. Instead, Android allows APKs to be signed with self-signed certificates, which is typical. (APK signing is discussed in Recipe 1-8.)

Activities in Depth

Activities are described by subclasses of the android.app.Activity class, which is an indirect subclass of the abstract android.content.Context class.

Note

Context is an abstract class whose methods let apps access global information about their environments (such as their resources and filesystems), and allow apps to perform contextual operations, such as launching activities and services, broadcasting intents, and opening private files.

Activity subclasses override various Activity lifecycle callback methods that Android calls during the life of an activity. For example, the SimpleActivity class in Listing 1-2 extends Activity and also overrides the void onCreate(Bundle bundle) and void onDestroy() lifecycle callback methods.

Example 1.2. A Skeletal Activity

import android.app.Activity;
import android.os.Bundle;

public class SimpleActivity extends Activity
{
   @Override
   public void onCreate(Bundle savedInstanceState)
   {
      super.onCreate(savedInstanceState); // Always call superclass method first.
      System.out.println("onCreate(Bundle) called");
   }
   @Override
   public void onDestroy()
   {
      super.onDestroy(); // Always call superclass method first.
      System.out.println("onDestroy() called");
   }
}

The overriding onCreate(Bundle) and onDestroy() methods in Listing 1-2 first invoke their superclass counterparts, a pattern that must be followed when overriding the void onStart(), void onRestart(), void onResume(), void onPause(), and void onStop() lifecycle callback methods.

  • onCreate(Bundle) is called when the activity is first created. This method is used to create the activity's user interface, create background threads as needed, and perform other global initialization. onCreate() is passed an android.os.Bundle object containing the activity's previous state, if that state was captured; otherwise, the null reference is passed. Android always calls the onStart() method after calling onCreate(Bundle).

  • onStart() is called just before the activity becomes visible to the user. Android calls the onResume() method after calling onStart() when the activity comes to the foreground, and calls the onStop() method after onStart() when the activity becomes hidden.

  • onRestart() is called after the activity has been stopped, just prior to it being started again. Android always calls onStart() after calling onRestart().

  • onResume() is called just before the activity starts interacting with the user. At this point, the activity has the focus and user input is directed to the activity. Android always calls the onPause() method after calling onResume(), but only when the activity must be paused.

  • onPause() is called when Android is about to resume another activity. This method is typically used to persist unsaved changes, stop animations that might be consuming processor cycles, and so on. It should perform its job quickly, because the next activity won't be resumed until it returns. Android calls onResume() after calling onPause() when the activity starts interacting with the user, and calls onStop() when the activity becomes invisible to the user.

  • onStop() is called when the activity is no longer visible to the user. This may happen because the activity is being destroyed, or because another activity (either an existing one or a new one) has been resumed and is covering the activity. Android calls onRestart() after calling onStop(), when the activity is coming back to interact with the user, and calls the onDestroy() method when the activity is going away.

  • onDestroy() is called before the activity is destroyed, unless memory is tight and Android is forced to kill the activity's process. In this scenario, onDestroy() is never called. If onDestroy() is called, it will be the final call that the activity ever receives.

Note

Android can kill the process hosting the activity at any time after onPause(), onStop(), or onDestroy() returns. An activity is in a killable state from the time onPause() returns until the time onResume() is called. The activity won't again be killable until onPause() returns.

These seven methods define an activity's entire lifecycle and describe the following three nested loops:

  • The entire lifetime of an activity is defined as everything from the first call to onCreate(Bundle) through to a single final call to onDestroy(). An activity performs all of its initial setup of "global" state in onCreate(Bundle), and releases all remaining resources in onDestroy(). For example, if the activity has a thread running in the background to download data from the network, it might create that thread in onCreate(Bundle) and stop the thread in onDestroy().

  • The visible lifetime of an activity is defined as everything from a call to onStart() through to a corresponding call to onStop(). During this time, the user can see the activity onscreen, although it might not be in the foreground interacting with the user. Between these two methods, the activity can maintain resources that are needed to show itself to the user. For example, it can register a broadcast receiver in onStart() to monitor for changes that impact its user interface, and unregister this object in onStop() when the user can no longer see what the activity is displaying. The onStart() and onStop() methods can be called multiple times, as the activity alternates between being visible to and being hidden from the user.

  • The foreground lifetime of an activity is defined as everything from a call to onResume() through to a corresponding call to onPause(). During this time, the activity is in front of all other activities onscreen and is interacting with the user. An activity can frequently transition between the resumed and paused states; for example, onPause() is called when the device goes to sleep or when a new activity is started, and onResume() is called when an activity result or a new intent is delivered. The code in these two methods should be fairly lightweight.

Note

Each lifecycle callback method is a hook that an activity can override to perform appropriate work. All activities must implement onCreate(Bundle) to carry out the initial setup when the activity object is first instantiated. Many activities also implement onPause() to commit data changes and otherwise prepare to stop interacting with the user.

Figure 1-3 illustrates an activity's lifecycle in terms of these seven methods.

The lifecycle of an activity reveals that there's no guarantee of onDestroy() being called.

Figure 1.3. The lifecycle of an activity reveals that there's no guarantee of onDestroy() being called.

Because onDestroy() might not be called, you should not count on using this method as a place for saving data. For example, if an activity is editing a content provider's data, those edits should typically be committed in onPause().

In contrast, onDestroy() is usually implemented to free resources (such as threads) that are associated with an activity so that a destroyed activity doesn't leave such things around while the rest of its app is still running.

Figure 1-3 reveals that an activity is started by calling startActivity(). More specifically, the activity is started by creating an Intent object describing an explicit or implicit intent, and by passing this object to Context's void startActivity(Intent intent) method (launch a new activity; no result is returned when it finishes).

Alternatively, the activity could be started by calling Activity's void startActivityForResult(Intent intent, int requestCode) method. The specified int result is returned to Activity's void onActivityResult(int requestCode, int resultCode, Intent data) callback method as an argument.

Note

The responding activity can look at the initial intent that caused it to be launched by calling Activity's Intent getIntent() method. Android calls the activity's void onNewIntent(Intent intent) method (also located in the Activity class) to pass any subsequent intents to the activity.

Suppose that you've created an app named SimpleActivity, and that this app consists of SimpleActivity (described in Listing 1-2) and SimpleActivity2 classes. Now suppose that you want to launch SimpleActivity2 from SimpleActivity's onCreate(Bundle) method. The following code fragment shows you how to start SimpleActivity2:

Intent intent = new Intent(SimpleActivity.this, SimpleActivity2.class);
SimpleActivity.this.startActivity(intent);

The first line creates an Intent object that describes an explicit intent. It initializes this object by passing the current SimpleActivity instance's reference and SimpleActivity2's Class instance to the Intent(Context packageContext, Class<?> cls) constructor.

The second line passes this Intent object to startActivity(Intent), which is responsible for launching the activity described by SimpleActivity2.class. If startActivity(Intent) was unable to find the specified activity (which shouldn't happen), it would throw an android.content.ActivityNotFoundException instance.

Activities must be declared in the app's AndroidManifest.xml file or they cannot be started (because they are invisible to Android). For example, the AndroidManifest.xml file in Listing 1-3 declares SimpleActivity and SimpleActivity2 – the ellipsis refers to content not relevant to this discussion.

Example 1.3. SimpleActivity's Manifest File

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.example.project" ...>
   <application ...>
      <activity android:name=".SimpleActivity" ...>
         <intent-filter ...>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
         </intent-filter>
      </activity>
      <activity android:name=".SimpleActivity2" ...>
          <intent-filter ...>
              <action android:name="android.intent.action.VIEW" />
              <data android:mimeType="image/jpeg" />
              <category android:name="android.intent.category.DEFAULT" />
          </intent-filter>
      </activity>
      ...
   </application>
</manifest>

Listing 1-3 reveals that each of SimpleActivity and SimpleActivity2 is associated with an intent filter via an <intent-filter> tag that's nested within <activity>. SimpleActivity2's <intent-filter> tag helps Android determine that this activity is to be launched when the Intent object's values match the following tag values:

  • <action>'s android:name attribute is assigned "android.intent.action.VIEW"

  • <data>'s android:mimeType attribute is assigned "image/jpeg" MIME type – additional attributes (such as android:path) would typically be present to locate the data to be viewed

  • <category>'s android:name attribute is assigned "android.intent.category.DEFAULT" to allow the activity to be launched without explicitly specifying its component.

The following code fragment shows you how to start SimpleActivity2 implicitly:

Intent intent = new Intent();
intent.setAction("android.intent.action.VIEW");
intent.setType("image/jpeg");
intent.addCategory("android.intent.category.DEFAULT");
SimpleActivity.this.startActivity(intent);

The first four lines create an Intent object describing an implicit intent. Values passed to Intent's Intent setAction(String action), Intent setType(String type), and Intent addCategory(String category) methods specify the intent's action, MIME type, and category. They help Android identify SimpleActivity2 as the activity to be launched.

Services in Depth

Services are described by subclasses of the abstract android.app.Service class, which is an indirect subclass of Context.

Service subclasses override various Service lifecycle callback methods that Android calls during the life of a service. For example, the SimpleService class in Listing 1-4 extends Service and also overrides the void onCreate() and void onDestroy() lifecycle callback methods.

Example 1.4. A Skeletal Service, Version 1

import android.app.Service;

public class SimpleService extends Service
{
   @Override
   public void onCreate()
   {
      System.out.println("onCreate() called");
   }
   @Override
   public void onDestroy()
   {
System.out.println("onDestroy() called");
   }
   @Override
   public IBinder onBind(Intent intent)
   {
      System.out.println("onBind(Intent) never called");
      return null;
   }
}

onCreate() is called when the service is initially created, and onDestroy() is called when the service is being removed. Because it is abstract, the IBinder onBind(Intent intent) lifecycle callback method (described later in this section) must always be overridden, even if only to return null, which indicates that this method is ignored.

Note

Service subclasses typically override onCreate() and onDestroy() to perform initialization and cleanup. Unlike Activity's onCreate(Bundle) and onDestroy() methods, Service's onCreate() method isn't repeatedly called and its onDestroy() method is always called.

A service's lifetime happens between the time onCreate() is called and the time onDestroy() returns. As with an activity, a service initializes in onCreate() and cleans up in onDestroy(). For example, a music playback service could create the thread that plays music in onCreate() and stop the thread in onDestroy().

Local services are typically started via Context's ComponentName startService(Intent intent) method, which returns an android.content.ComponentName instance that identifies the started service component, or the null reference if the service doesn't exist. Furthermore, startService(Intent) results in the lifecycle shown in Figure 1-4.

The lifecycle of a service that's started by startService(Intent) features a call to onStartCommand(Intent, int, int).

Figure 1.4. The lifecycle of a service that's started by startService(Intent) features a call to onStartCommand(Intent, int, int).

The call to startService(Intent) results in a call to onCreate(), followed by a call to int onStartCommand(Intent intent, int flags, int startId). This latter lifecycle callback method, which replaces the deprecated void onStart(Intent intent, int startId) method, is called with the following arguments:

  • intent is the Intent object passed to startService(Intent).

  • flags can provide additional data about the start request, but are often set to 0.

  • startID is a unique integer that describes this start request. A service can pass this value to Service's boolean stopSelfResult(int startId) method to stop itself.

onStartCommand(Intent, int, int) processes the Intent object, and typically returns the constant Service.START_STICKY to indicate that the service is to continue running until explicitly stopped. At this point, the service is running and will continue to run until one of the following events occurs:

  • Another component stops the service by calling Context's boolean stopService(Intent intent) method. Only one stopService(Intent) call is needed no matter how often startService(Intent) was called.

  • The service stops itself by calling one of Service's overloaded stopSelf() methods, or by calling Service's stopSelfResult(int) method.

After stopService(Intent), stopSelf(), or stopSelfResult(int) has been called, Android calls onDestroy() to let the service perform cleanup tasks.

Note

When a service is started by calling startService(Intent), onBind(Intent) is not called.

Listing 1-5 presents a skeletal service class that could be used in the context of the startService(Intent) method.

Example 1.5. A Skeletal Service, Version 2

import android.app.Service;

public class SimpleService extends Service
{
   @Override
   public void onCreate()
   {
      System.out.println("onCreate() called");
   }
   @Override
   public int onStartCommand(Intent intent, int flags, int startId)
   {
      System.out.println("onStartCommand(Intent, int, int) called");
      return START_STICKY;
   }
   @Override
   public void onDestroy()
   {
      System.out.println("onDestroy() called");
   }
@Override
   public IBinder onBind(Intent intent)
   {
      System.out.println("onBind(Intent) never called");
      return null;
   }
}

The following code fragment, which is assumed to be located in the onCreate() method of Listing 1-2's SimpleActivity class, employs startService(Intent) to start an instance of Listing 1-5's SimpleService class via an explicit intent:

Intent intent = new Intent(SimpleActivity.this, SimpleService.class);
SimpleActivity.this.startService(intent);

Remote services are started via Context's boolean bindService(Intent service, ServiceConnection conn, int flags) method, which connects to a running service, creating the service if necessary, and which returns 'true' when successfully connected. bindService(Intent, ServiceConnection, int) results in the lifecycle illustrated by Figure 1-5.

The lifecycle of a service started by bindService(Intent, ServiceConnection, int) doesn't include a call to onStartCommand(Intent, int, int).

Figure 1.5. The lifecycle of a service started by bindService(Intent, ServiceConnection, int) doesn't include a call to onStartCommand(Intent, int, int).

The call to bindService(Intent, ServiceConnection, int) results in a call to onCreate() followed by a call to onBind(Intent), which returns the communications channel (an instance of a class that implements the android.os.IBinder interface) that clients use to interact with the service.

The client interacts with the service as follows:

  1. The client subclasses android.content.ServiceConnection and overrides this class's abstract void onServiceConnected(ComponentName className, IBinder service) and void onServiceDisconnected(ComponentName name) methods in order to receive information about the service as the service is started and stopped. When bindService(Intent, ServiceConnection, int) returns true, the former method is called when a connection to the service has been established; the IBinder argument passed to this method is the same value returned from onBind(Intent). The latter method is called when a connection to the service has been lost.

    Lost connections typically occur when the process hosting the service has crashed or has been killed. The ServiceConnection instance itself is not removed – the binding to the service will remain active, and the client will receive a call to onServiceConnected(ComponentName, IBinder) when the service is next running.

  2. The client passes the ServiceConnection subclass object to bindService(Intent, ServiceConnection, int).

A client disconnects from a service by calling Context's void unbindService(ServiceConnection conn) method. This component no longer receives calls as the service is restarted. If no other components are bound to the service, the service is allowed to stop at any time.

Before the service can stop, Android calls the service's boolean onUnbind(Intent intent) lifecycle callback method with the Intent object that was passed to unbindService(ServiceConnection). Assuming that onUnbind(Intent) doesn't return 'true,' which tells Android to call the service's void onRebind(Intent intent) lifecycle callback method each time a client subsequently binds to the service, Android calls onDestroy() to destroy the service.

Listing 1-6 presents a skeletal service class that could be used in the context of the bindService(Intent, ServiceConnection, int) method.

Example 1.6. A Skeletal Service, Version 3

import android.app.Service;

public class SimpleService extends Service
{
   public class SimpleBinder extends Binder
   {
      SimpleService getService()
      {
         return SimpleService.this;
      }
   }
   private final IBinder binder = new SimpleBinder();
   @Override
public IBinder onBind(Intent intent)
   {
      return binder;
   }
   @Override
   public void onCreate()
   {
      System.out.println("onCreate() called");
   }
   @Override
   public void onDestroy()
   {
      System.out.println("onDestroy() called");
   }
}

Listing 1-6 first declares a SimpleBinder inner class that extends the android.os.Binder class. SimpleBinder declares a single SimpleService getService() method that returns an instance of the SimpleService subclass.

Note

Binder works with the IBinder interface to support a remote procedure call mechanism for communicating between processes. Although this example assumes that the service is running in the same process as the rest of the app, Binder and IBinder are still required.

Listing 1-6 next instantiates SimpleBinder and assigns the instance's reference to the private binder field. This field's value is returned from the subsequently overriding onBind(Intent) method.

Let's assume that the SimpleActivity class in Listing 1-2 declares a private SimpleService field named ss (private SimpleService ss;). Continuing, let's assume that the following code fragment is contained in SimpleActivity's onCreate(Bundle) method:

ServiceConnection sc = new ServiceConnection()
{
   public void onServiceConnected(ComponentName className, IBinder service)
   {
      ss = ((SimpleService.SimpleBinder) service).getService();
      System.out.println("Service connected");
   }
   public void onServiceDisconnected(ComponentName className)
   {
      ss = null; System.out.println("Service disconnected");
   }
};
bindService(new Intent(SimpleActivity.this, SimpleService.class), sc,
            Context.BIND_AUTO_CREATE);

This code fragment first instantiates a ServiceConnection subclass. The overriding onServiceConnected(ComponentName, IBinder) method concerns itself with using the service argument to call SimpleBinder's getService() method and save the result.

Although it must be present, the overriding onServiceDisconnected(ComponentName) method should never be called, because SimpleService runs in the same process as SimpleActivity.

The code fragment next passes the ServiceConnection subclass object, along with an intent identifying SimpleService as the intent's target and Context.BIND_AUTO_CREATE (create a persistent connection), to bindService(Intent, ServiceConnection, int).

Note

A service can be started (with startService(Intent)) and have connections bound to it (with bindService(Intent, ServiceConnection, int). In this situation, Android keeps the service running as long as it's started, or one or more connections with the BIND_AUTO_CREATE flag have been made to the service. Once neither of these situations holds, the service's onDestroy() method is called and the service is terminated. All cleanup work, such as stopping threads or unregistering broadcast receivers, should be finished upon returning from onDestroy().

Regardless of how you start the service, the app's AndroidManifest.xml file must have an entry for this component. The following entry declares SimpleService:

<service android:name=".SimpleService">
</service>

Note

Although the previous example used bindService(Intent, ServiceConnection, int) to start a local service, it's more typical to use this method to start a remote service. Chapter 5 introduces you to remote services.

Broadcast Receivers in Depth

Broadcast receivers are described by classes that subclass the abstract android.content.BroadcastReceiver class and override BroadcastReceiver's abstract void onReceive(Context context, Intent intent) method. For example, the SimpleBroadcastReceiver class in Listing 1-7 extends BroadcastReceiver and overrides this method.

Example 1.7. A Skeletal Broadcast Receiver

public class SimpleBroadcastReceiver extends BroadcastReceiver
{
   @Override
   public void onReceive(Context context, Intent intent)
   {
      System.out.println("onReceive(Context, Intent) called");
   }
}

You start a broadcast receiver by creating an Intent object and passing this object to any of Context's broadcast methods (such as Context's overloaded sendBroadcast() methods), which broadcast the message to all interested broadcast receivers.

The following code fragment, which is assumed to be located in the onCreate() method of Listing 1-2's SimpleActivity class, starts an instance of Listing 1-7's SimpleBroadcastReceiver class:

Intent intent = new Intent(SimpleActivity.this, SimpleBroadcastReceiver.class);
intent.putExtra("message", "Hello, broadcast receiver!");
SimpleActivity.this.sendBroadcast(intent);

Intent's Intent putExtra(String name, String value) method is called to store the message as a key/value pair. As with Intent's other putExtra() methods, this method returns a reference to the Intent object so that method calls can be chained together.

Unless you create a broadcast receiver dynamically, AndroidManifest.xml must have an entry for this component. The following entry declares SimpleBroadcastReceiver:

<receiver android:name=".SimpleBroadcastReceiver">
</receiver>

Content Providers in Depth

Content providers are described by classes that subclass the abstract android.content.ContentProvider class and override ContentProvider's abstract methods (such as String getType(Uri uri)). For example, the SimpleContentProvider class in Listing 1-8 extends ContentProvider and overrides these methods.

Example 1.8. A Skeletal Content Provider

public class SimpleContentProvider extends ContentProvider
{
   @Override
   public int delete(Uri uri, String selection, String[] selectionArgs)
   {
      System.out.println("delete(Uri, String, String[]) called");
      return 0;
   }
   @Override
   public String getType(Uri uri)
   {
      System.out.println("getType(Uri) called");
      return null;
   }
   @Override
   public Uri insert(Uri uri, ContentValues values)
   {
      System.out.println("insert(Uri, ContentValues) called");
      return null;
   }
   @Override
   public boolean onCreate()
   {
      System.out.println("onCreate() called");
return false;
   }
   @Override
   public Cursor query(Uri uri, String[] projection, String selection,
                       String[] selectionArgs, String sortOrder)
   {
      System.out.println("query(Uri, String[], String, String[], String) called");
      return null;
   }
   @Override
   public int update(Uri uri, ContentValues values, String selection,
                     String[] selectionArgs)
   {
      System.out.println("update(Uri, ContentValues, String, String[]) called");
      return 0;
   }
}

Clients don't instantiate SimpleContentProvider and call these methods directly. Rather, they instantiate a subclass of the abstract android.content.ContentResolver class and call its methods (such as public final Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)).

Note

A ContentResolver instance can talk to any content provider; it cooperates with the provider to manage any interprocess communication that's involved.

AndroidManifest.xml must have to an entry for this component. The following entry declares SimpleContentProvider:

<provider android:name=".SimpleContentProvider">
</provider>

1-1. Installing the Android SDK

Problem

You've read the previous introduction to Android and are eager to develop your first Android app. However, you must install Android SDK 2.3 before you can develop apps.

Solution

Google provides an Android SDK 2.3 distribution file for each of the Windows, Intel-based Mac OS X, and Linux operating systems. Download and unarchive the appropriate file for your platform and move its unarchived home directory to a convenient location. You might also want to update your PATH environment variable so that you can access the SDK's command-line tools from anywhere in your filesystem.

Before downloading and installing this file, you must be aware of SDK requirements. You cannot use the SDK if your development platform doesn't meet these requirements.

Android SDK 2.3 supports the following operating systems:

  • Windows XP (32-bit), Vista (32- or 64-bit), or Windows 7 (32- or 64-bit)

  • Mac OS X 10.5.8 or later (x86 only)

  • Linux (tested on Ubuntu Linux, Lucid Lynx): GNU C Library (glibc) 2.11 or later is required. 64-bit distributions must be able to run 32-bit applications. To learn how to add support for 32-bit applications, see the Ubuntu Linux installation notes at http://developer.android.com/sdk/installing.html#troubleshooting.

You'll quickly discover that Android SDK 2.3 is organized into various components: SDK tools, SDK Platform tools, different versions of the Android platform (also known as the Android software stack), SDK add-ons, USB driver for Windows, samples, and offline documentation. Each component requires a minimum amount of disk storage space; the total required amount of space depends upon which components you choose to install:

  • SDK Tools: The SDK's tools require approximately 35MB of disk storage space and must be installed.

  • SDK Platform Tools: The SDK's platform tools require approximately 6MB of disk storage space and must be installed.

  • Android platform: Each Android platform corresponds to a specific version of Android and requires approximately 150MB of disk storage space. At least one Android platform must be installed.

  • SDK Add-on: Each optional SDK add-on (such as Google APIs or a third-party vendor's API libraries) requires approximately 100MB of disk storage space.

  • USB Driver for Windows: The optional USB driver for the Windows platform requires approximately 10MB of disk storage space. If you're developing on Mac OS X or Linux, you don't need to install the USB driver.

  • Samples: Each Android platform's optional app examples require approximately 10MB of disk storage space.

  • Offline documentation: Instead of having to be online to access the Android documention, you can choose to download the documentation so that you can view it even when not connected to the Internet. The offline documentation requires approximately 250MB of disk storage space.

Finally, you should ensure that the following additional software is installed:

  • JDK 5 or JDK 6: You need to install one of these Java Development Kits (JDKs) to compile Java code. It's not sufficient to have only a Java Runtime Environment (JRE) installed.

  • Apache Ant: You need to install Ant version 1.6.5 or later for Linux and Mac, and Ant version 1.7 or later for Windows so that you can build Android projects.

Note

If a JDK is already installed on your development platform, take a moment to ensure that it meets the previously listed version requirement (5 or 6). Some Linux distributions may include JDK 1.4, which is not supported for Android development. Also, Gnu Compiler for Java is not supported.

How It Works

Point your browser to http://developer.android.com/sdk/index.html and download one of android-sdk_r08-windows.zip (Windows), android-sdk_r08-mac_86.zip (Mac OS X), and android-sdk_r08-linux_86.tgz (Linux).

Note

Windows developers have the option of downloading and running installer_r08-windows.exe. This tool automates must of the installation process.

For example, if you run Windows XP, download android-sdk_r08-windows.zip. After unarchiving this file, move the unarchived android-windows-sdk home directory to a convenient location in your filesystem; for example, you might move the unarchived C:unzippedandroid-sdk_r08-windowsandroid-sdk-windows home directory to the root directory on your C: drive, resulting in C:android-sdk-windows.

Note

To complete installation, add the tools subdirectory to your PATH environment variable so that you can access the SDK's command-line tools from anywhere in your filesystem.

A subsequent examination of android-windows-sdk shows that this home directory contains the following subdirectories and files:

  • add-ons: This initially empty directory stores add-ons from Google and other vendors; for example, the Google APIs add-on is stored here.

  • platforms: This initially empty directory stores Android platforms in separate subdirectories. For example, Android 2.3 would be stored in one platforms subdirectory, whereas Android 2.2 would be stored in another platforms subdirectory.

  • tools: This directory contains a set of platform-independent development and profiling tools. The tools in this directory may be updated at any time, independent of Android platform releases.

  • SDK Manager.exe: A special tool that launches the Android SDK and AVD Manager tool, which you use to add components to your SDK.

  • SDK Readme.txt: Tells you how to perform the initial setup of your SDK, including how to launch the Android SDK and AVD Manager tool on all platforms.

The tools directory contains a variety of useful tools, including the following:

  • android: Creates and updates Android projects; updates the Android SDK with new platforms, add-ons, and documentation; and creates, deletes, and views Android Virtual Devices (discussed in Recipe 1-3).

  • emulator: Runs a full Android software stack down to the kernel level, and includes a set of preinstalled apps (such as Browser) that you can access.

  • sqlite3: Manages SQLite databases created by Android apps.

  • zipalign: Performs archive alignment optimization on APK files.

1-2. Installing an Android Platform

Problem

Installing the Android SDK is insufficient for developing Android apps; you must also install at least one Android platform.

Solution

Use the SDK Manager tool to install an Android platform.

How It Works

Run SDK Manager. This tool presents the Android SDK and AVD Manager dialog box, followed by the Refresh Sources and Choose Packages to Install dialog boxes.

Android SDK and AVD Manager identifies virtual devices, installed packages, and available packages. It also lets you configure proxy server and other settings.

When this dialog box appears, the Installed packages entry in the list appearing on the right side of the dialog box is highlighted, and the pane to the right of that list identifies all packages that have been installed. If you're installing Android for the first time, this pane reveals that only the Android SDK tools (revision 8) component has been installed.

Note

You can also use the android tool to display the Android SDK and AVD Manager dialog box. Accomplish this task by specifying android by itself on the command line. When displayed in this manner, Android SDK and AVD Manager highlights Virtual devices instead of Installed packages.

After presenting this dialog box, SDK Manager scans Google's servers for available component packages to install. The Refresh Sources dialog box reveals its progress.

After SDK Manager finishes its scan, it presents the Choose Packages to Install dialog box (see Figure 1-6) to let you choose those SDK components you want to install.

The Packages list identifies those packages that can be installed.

Figure 1.6. The Packages list identifies those packages that can be installed.

Note

Google recommends that you disable any active antivirus software before installing SDK components. Otherwise, you'll probably encounter an SDK Manager: failed to install dialog box telling you that a folder could not be renamed or moved, and telling you to momentarily disable your antivirus software before clicking the dialog box's Yes button to try again.

The Choose Packages to Install dialog box shows a Packages list that identifies those packages that can be installed. It displays checkmarks beside packages that have been accepted for installation, and displays Xs beside those packages that have been rejected for installation.

For the highlighted package, Package Description & License presents a package description, a list of other packages that are dependent on this package being installed, information about the archive that houses the package, and additional information. Also, you can select a radio button to accept or reject the package.

Note

In some cases, an SDK component may require a specific minimum revision of another component or SDK tool. In addition to Package Description & License documenting these dependencies, the development tools will notify you with debug warnings if there's a dependency that you need to address.

Because this book focuses on Android 2.3, the only packages that you need to install are Android SDK Platform-tools, revision 1 and SDK Platform Android 2.3, API 9, revision 1. All other checked package entries can be unchecked by clicking the Reject radio button on their respective panes.

Note

If you plan to develop apps that will run on devices with earlier versions of Android, you might want to leave the checkmarks beside those versions. However, it's not necessary to do so at this point; you can always come back later and add those versions via SDK Manager.

After making sure that only these entries are checked, click the Install button to begin installation. Figure 1-7 shows you the resulting Installing Archives dialog box.

The Installing Archives dialog box reveals the progress of downloading and installing each selected package archive.

Figure 1.7. The Installing Archives dialog box reveals the progress of downloading and installing each selected package archive.

You'll probably encounter the ADB Restart dialog box, which tells you that a package dependent on Android Debug Bridge (ADB) has been updated, and asking you whether you want to restart ADB now. Click the Yes button, which closes ADB Restart, then click Close on the Installing Archives dialog box.

You should now observe the Android SDK and AVD Manager's Installed packages pane also displaying Android SDK Platform-tools, revision 1 and SDK Platform Android 2.3, API 9, revision 1 in addition to Android SDK Tools, revision 8. You should also observe the following new subdirectories:

  • platform-tools (in android-sdk-windows)

  • android-9 (in android-sdk-windows/platforms)

platform-tools contains development tools that may be updated with each platform release. Its tools include aapt (Android Asset Packaging Tool – view, create, and update Zip-compatible archives (.zip, .jar, .apk); and compile resources into binary assets), adb (Android Debug Bridge – manage the state of an emulator instance or an Android-powered device), and dx (Dalvik Executable – generate Android bytecode from Java .class files). android-9 stores Android 2.3 data and user interface-oriented files.

Tip

You might want to add platform-tools to your PATH environment variable so that you can access these tools from anywhere in your filesystem.

1-3. Creating an Android Virtual Device

Problem

After installing the Android SDK and an Android platform, you're ready to start creating Android apps. However, you won't be able to run those apps via the emulator tool until you create an Android Virtual Device (AVD), a device configuration that represents an Android device.

Solution

Use the SDK Manager tool to create an AVD.

How It Works

Run SDK Manager if necessary. Click the Android SDK and AVD Manager dialog box's Virtual devices entry in the list on the left. You should see the pane shown in Figure 1-8.

No AVDs are initially installed.

Figure 1.8. No AVDs are initially installed.

Click the New button. Figure 1-9 shows you the resulting Create new Android Virtual Device (AVD) dialog box.

An AVD consists of a name, a target platform, an SD Card, a skin, and hardware properties.

Figure 1.9. An AVD consists of a name, a target platform, an SD Card, a skin, and hardware properties.

Figure 1-9 reveals that an AVD has a name, targets a specific Android platform, can emulate an SD card, and provides a skin with a certain screen resolution. Enter test_AVD for the name, select Android 2.3 – API Level 9 for the target platform, and enter 100 into the Size field for the SD card. Selecting Android 2.3 – API Level 9 results in Default (HVGA) being selected for the skin with an Abstracted LCD density property set to 160 dots per inch (dpi).

Note

If you've installed Android 2.3.1, selecting Android 2.3.1 – API Level 9 results in Default (WVGA800) being selected for the skin with an Abstracted LCD density property set to 240 dpi. Furthermore, a Max VM application heap size property set to 24 megabytes is also present.

After entering the previous values and keeping the screen defaults, finish AVD creation by clicking Create AVD. The AVD pane in Figure 1-8 will now include an entry for test_AVD.

Warning

When creating an AVD that you plan to use to test compiled apps, make sure that the target platform has an API level greater than or equal to the API level required by your app. In other words, if you plan to test your app on the AVD, your app cannot access platform APIs that are more recent than those APIs supported by the AVD's API level.

Although it's easier to use SDK Manager to create an AVD, you can also accomplish this task via the android tool by specifying android create avd -n name -t targetID [-option value].... Given this syntax, name identifies the device configuration (such as target_AVD), targetID is an integer ID that identifies the targeted Android platform (you can obtain this integer ID by executing android list targets), and [-option value]... identifies a series of options (such as SD card size).

If you don't specify sufficient options, android prompts to create a custom hardware profile. Press the Enter key if you don't want a custom hardware profile and prefer to use the default hardware emulation options. For example, the android create avd -n test_AVD -t 1 command line causes an AVD named test_AVD to be created. This command line assumes that 1 corresponds to the Android 2.3 platform and prompts to create a custom hardware profile.

Note

Each AVD functions as an independent device with its own private storage for user data, its own SD card, and so on. When you launch the emulator tool with an AVD, this tool loads user data and SD card data from the AVD's directory. By default, emulator stores user data, SD card data, and a cache in the directory assigned to the AVD.

1-4. Starting the AVD

Problem

You must start the AVD, which can take a few minutes to get started, before you can install and run apps on it, and want to know how to accomplish this task.

Solution

Use the SDK Manager tool to start the AVD. Or, start the AVD by using the emulator tool.

How It Works

Refer to Figure 1-8 and you'll notice a disabled Start button. This button is no longer disabled after an AVD entry is created. Click Start to run the emulator tool with the highlighted AVD entry as the emulator's device configuration.

A Launch Options dialog box appears. This dialog box identifies the AVD's skin and screen density. It also provides unchecked checkboxes for scaling the resolution of the emulator's display to match the physical device's screen size, and for wiping user data.

Note

As you update your apps, you'll periodically package and install them on the emulator, which preserves the apps and their state data across AVD restarts in a user-data disk partition. To ensure that an app runs properly as you update it, you might need to delete the emulator's user-data partition, which is accomplished by checking Wipe user data.

Click the Launch button to launch the emulator with the AVD. SDK Manager responds by briefly displaying a Starting Android Emulator dialog box, followed by command windows (on Windows XP), and by finally displaying the emulator window.

The emulator window is divided into a left pane that displays the Android logo on a black background followed by the home screen, and a right pane that displays phone controls and a keyboard. Figure 1-10 shows these panes for the test_AVD device.

The emulator window presents the home screen on the left, and phone controls and a keyboard on the right.

Figure 1.10. The emulator window presents the home screen on the left, and phone controls and a keyboard on the right.

If you've previously used an Android device, you're probably familiar with the home screen, the phone controls, and the keyboard. If not, there are a few items to keep in mind:

  • The home screen is a special app that serves as a starting point for using an Android device.

  • A status bar appears above the home screen (and every app screen). The status bar presents the current time, amount of battery power remaining, and other information; and also provides access to notifications.

  • The home screen presents a wallpaper background. Click the MENU button in the phone controls followed by Wallpaper in the popup menu to change the wallpaper.

  • The home screen presents the Google Search widget near the top. A widget is a miniature app view that can be embedded in the home screen and other apps, and receives periodic updates.

  • The home screen presents the app launcher near the bottom. The launcher presents icons for launching the commonly used Phone and Browser apps, and for displaying a rectangular grid of all installed apps, which are subsequently launched by double-clicking their icons.

  • The home screen consists of multiple panes. Click the dots on either side of the app launcher to replace the current pane with the next pane to the left or right – the number of dots indicate the number of panes remaining to be visited to the left or right. Or, press and hold down the mouse pointer over the middle icon on the app launcher to bring up a list of miniature pane icons; click one of these icons to display the corresponding home screen pane.

  • The house icon phone control button takes you from wherever you are to the home screen.

  • The MENU phone control button presents a menu of app-specific choices for the currently running app.

  • The curved arrow icon phone control button takes you back to the previous activity in the activity stack.

While the AVD is running, you can interact with it by using your mouse to "touch" the touchscreen and your keyboard to "press" the AVD keys. Table 1-2 shows you the mappings between AVD keys and keyboard keys.

Table 1.2. Mappings Between AVD Keys and Keyboard Keys

AVD Key

Keyboard Key

Home

HOME

Menu (left softkey)

F2 or Page Up

Star (right softkey)

Shift-F2 orPage Down

Back

ESC

Call/dial button

F3

Hangup/end call button

F4

Search

F5

Power button

F7

Audio volume up button

KEYPAD_PLUS, Ctrl-5

Audio volume down button

KEYPAD_MINUS, Ctrl-F6

Camera button

Ctrl-KEYPAD_5, Ctrl-F3

Switch to previous layout orientation (portrait or landscape)

KEYPAD_7, Ctrl-F11

Switch to next layout orientation

KEYPAD_9, Ctrl-F12

Toggle cell networking on/off

F8

Toggle code profiling

F9 (only with -trace startup option)

Toggle fullscreen mode

Alt-Enter

Toggle trackball mode

F6

Enter trackball mode temporarily (while key is pressed)

Delete

DPad left/up/right/down

KEYPAD_4/8/6/2

DPad center click

KEYPAD_5

Onion alpha increase/decrease

KEYPAD_MULTIPLY(*) / KEYPAD_DIVIDE(/)

Tip

You must first disable NumLock on your development computer before you can use keypad keys.

Table 1-2 refers to the -trace startup option in the context of toggle code profiling. This option lets you store profiling results in a file when starting the AVD via the emulator tool.

For example, emulator -avd test_AVD-trace results.txt starts the emulator for device configuration test_AVD, and also stores profiling results in results.txt when you press F9. Press F9 again to stop code profiling.

Figure 1-10 displays 5554:test_AVD in the titlebar. The 5554 value identifies a console port that you can use to dynamically query and otherwise control the environment of the AVD.

Note

Android supports up to 16 concurrently executing AVDs. Each AVD is assigned an even-numbered console port number starting with 5554.

You can connect to the AVD's console by specifying telnet localhost console-port. For example, specify telnet localhost 5554 to connect to test_AVD's console. Figure 1-11 shows you the resulting command window on Windows XP.

Type a command name by itself for command-specific help.

Figure 1.11. Type a command name by itself for command-specific help.

1-5. Introducing UC

Problem

Now that you've installed the Android SDK, installed an Android platform, and created and started an AVD, you're ready to create an app, and install and run this app on the AVD. Although you could create an app based on Listing 1-2's SimpleActivity class, you'll probably find this recipe's UC app to be more interesting (and useful).

Solution

UC (an acronym for Units Converter) is an app that lets you convert between types of units. For example, you can convert a specific number of degrees Celsius to its equivalent number of degrees Fahrenheit, a specific number of pounds to its equivalent number of kilograms, and so on.

How It Works

UC consists of a single activity (also named UC) that presents a user interface (revealed in Recipe 1-7) consisting of an input/output textfield for entering the number of units to convert and displaying the conversion result, a spinner for choosing a conversion, and buttons for clearing the textfield, performing the conversion, and closing the app.

Listing 1-9 presents the UC activity's source code.

Example 1.9. An Activity for Performing Unit Conversions

// UC.java

package com.apress.uc;

import android.app.Activity;

import android.os.Bundle;

import android.text.Editable;
import android.text.TextWatcher;

import android.view.View;

import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Spinner;

public class UC extends Activity
{
   private int position = 0;

   private double[] multipliers =
{
      0.0015625,         // Acres to square miles
      101325.0,          // Atmospheres to Pascals
      100000.0,          // Bars to Pascals
      0,                 // Degrees Celsius to Degrees Fahrenheit (placeholder)
      0,                 // Degrees Fahrenheit to Degrees Celsius (placeholder)
      0.00001,           // Dynes to Newtons
      0.3048,            // Feet/Second to Metres/Second
      0.0284130625,      // Fluid Ounces (UK) to Litres
      0.0295735295625,   // Fluid Ounces (US) to Litres
      746.0,             // Horsepower (electric) to Watts
      735.499,           // Horsepower (metric) to Watts
      1/1016.0469088,    // Kilograms to Tons (UK or long)
      1/907.18474,       // Kilograms to Tons (US or short)
      1/0.0284130625,    // Litres to Fluid Ounces (UK)
      1/0.0295735295625, // Litres to Fluid Ounces (US)
      331.5,             // Mach Number to Metres/Second
      1/0.3048,          // Metres/Second to Feet/Second
      1/331.5,           // Metres/Second to Mach Number
      0.833,             // Miles/Gallon (UK) to Miles/Gallon (US)
      1/0.833,           // Miles/Gallon (US) to Miles/Gallon (UK)
      100000.0,          // Newtons to Dynes
      1/101325.0,        // Pascals to Atmospheres
      0.00001,           // Pascals to Bars
      640.0,             // Square Miles to Acres
      1016.0469088,      // Tons (UK or long) to Kilograms
      907.18474,         // Tons (US or short) to Kilograms
      1/746.0,           // Watts to Horsepower (electic)
      1/735.499          // Watts to Horsepower (metric)
   };

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

      final EditText etUnits = (EditText) findViewById(R.id.units);

      final Spinner spnConversions = (Spinner) findViewById(R.id.conversions);
      ArrayAdapter<CharSequence> aa;
      aa = ArrayAdapter.
             createFromResource(this, R.array.conversions,
                                android.R.layout.simple_spinner_item);
      aa.setDropDownViewResource(android.R.layout.simple_spinner_item);
      spnConversions.setAdapter(aa);

      AdapterView.OnItemSelectedListener oisl;
      oisl = new AdapterView.OnItemSelectedListener()
      {
         @Override
         public void onItemSelected(AdapterView<?> parent, View view,
                                    int position, long id)
         {
            UC.this.position = position;
         }
@Override
         public void onNothingSelected(AdapterView<?> parent)
         {
            System.out.println("nothing");
         }
      };
      spnConversions.setOnItemSelectedListener(oisl);

      final Button btnClear = (Button) findViewById(R.id.clear);
      AdapterView.OnClickListener ocl;
      ocl = new AdapterView.OnClickListener()
      {
         @Override
         public void onClick(View v)
         {
            etUnits.setText("");
         }
      };
      btnClear.setOnClickListener(ocl);
      btnClear.setEnabled(false);

      final Button btnConvert = (Button) findViewById(R.id.convert);
      ocl = new AdapterView.OnClickListener()
      {
         @Override
         public void onClick(View v)
         {
            String text = etUnits.getText().toString();
            double input = Double.parseDouble(text);
            double result = 0;
            if (position == 3)
               result = input*9.0/5.0+32; // Celsius to Fahrenheit
            else
            if (position == 4)
               result = (input-32)*5.0/9.0; // Fahrenheit to Celsius
            else
               result = input*multipliers[position];
            etUnits.setText(""+result);
         }
      };
      btnConvert.setOnClickListener(ocl);
      btnConvert.setEnabled(false);

      Button btnClose = (Button) findViewById(R.id.close);
      ocl = new AdapterView.OnClickListener()
      {
         @Override
         public void onClick(View v)
         {
            finish();
         }
      };
      btnClose.setOnClickListener(ocl);

      TextWatcher tw;
      tw = new TextWatcher()
      {
@Override
         public void afterTextChanged(Editable s)
         {
         }

         @Override
         public void beforeTextChanged(CharSequence s, int start, int count,
                                       int after)
         {
         }

         @Override
         public void onTextChanged(CharSequence s, int start, int before,
                                   int count)
         {
            if (etUnits.getText().length() == 0)
            {
               btnClear.setEnabled(false);
               btnConvert.setEnabled(false);
            }
            else
            {
               btnClear.setEnabled(true);
               btnConvert.setEnabled(true);
            }
         }
      };
      etUnits.addTextChangedListener(tw);
   }
}

Listing 1-9 begins with a comment that conveniently identifies the source file (UC.java) describing the activity. This listing next presents a package statement that names the package (com.apress.uc), in which the source file's UC class is stored, followed by a series of import statements that import various Android API types.

Tip

You should familiarize yourself with the Android API's package organization so that you can quickly find API types in Google's Android API reference (http://developer.android.com/reference/packages.html). You'll want to locate documentation on these types quickly as you dig deeper into Android app development.

Listing 1-9 next describes the UC class, which extends Activity. This class first declares position and multipliers fields:

  • position stores the zero-based index of the conversion selected via the spinner, and defaults to 0 (the first conversion displayed by the spinner). Storing the spinner's position in this field simplifies choosing an appropriate conversion to perform.

  • multipliers stores an array of multiplier values, with each entry corresponding to a spinner value. A conversion is performed by multiplying the input value by multipliers[position]. However, there are two exceptions: Celsius-to-Fahrenheit and Fahrenheit-to-Celsius. These conversions are handled separately, because they also require an addition or a subtraction operation.

All of the app's work takes place in the overriding onCreate(Bundle) method: no other methods are required, which helps to keep this app simple.

onCreate(Bundle) first invokes its same-named superclass method, a rule that must be followed by all overriding activity methods.

This method then executes setContentView(R.layout.main) to establish the app's user interface.

R.layout.main identifies a resource, a piece of data required by an app's code, and which you maintain independently of the code by storing it in a separate file.

Note

Resources simplify app maintenance, make it easier to adapt a user interface to different screen sizes, and facilitate adapting an app to different languages.

You interpret this resource ID as follows:

  • R is the name of a class that's generated (by the aapt tool) when the app is being built. This class is named R because its content identifies various kinds of resources (such as layouts, images, strings, and colors).

  • layout is the name of a class that's nested within R. All resources whose IDs are stored in this class describe specific layout resources. Each kind of resource is associated with a nested class that's named in a similar fashion. For example, string identifies string resources.

  • main is the name of an int constant declared within layout. This resource ID identifies the main layout resource. Specifically, main refers to a main.xml file that stores the main screen's layout information. main is UC's only layout resource.

R.layout.main is passed to Activity's void setContentView(int layoutResID) method to tell Android to create a user interface screen using the layout information stored in main.xml. Behind the scenes, Android creates the user interface components described in main.xml and positions them on the screen as specified by main.xml's layout data.

This user interface is based on views (abstractions of user interface components) and view groups (views that group related user interface components). Views are instances of classes that subclass the android.view.View class and are analogous to Java components. View groups are instances of classes that subclass the abstract android.view.ViewGroup class and are analogous to Java containers. Android refers to specific views (such as buttons or spinners) as widgets.

Note

Don't confuse widget in this context with widgets shown on the Android home screen. Although the same term is used, user interface widgets and home screen widgets are different.

Continuing, onCreate(Bundle) executes final EditText etUnits = (EditText) findViewById(R.id.units);. This statement first calls View's View findViewById(int id) method to find the EditText view declared in main.xml and identified as units, and instantiate android.widget.EditText and initialize it to this view's declarative information, and then saves this object's reference in local variable etUnits. This variable is final because it's subsequently accessed from an anonymous inner class.

In a similar manner, final Spinner spnConversions = (Spinner) findViewById(R.id.conversions); instantiates the android.widget.Spinner class using the declarative information that's stored in main.xml, and saves the resulting object reference for subsequent access.

Note

Although it's preferable from a maintenance perspective to declare user interface screens via layout resources and let Android take care of creating widgets and adding them to layouts on your behalf, Android gives you the option of creating widgets and laying them out programmatically when you need to do so.

onCreate(Bundle) next addresses the spinner object having no text to display, by first calling the android.widget.ArrayAdapter class's ArrayAdapter<CharSequence> createFromResource(Context context, int textArrayResId, int textViewResId) method, which returns an array adapter that supplies text messages to the spinner:

  • context requires a Context instance that identifies the current app component, which happens to be the current activity as specified by keyword this.

  • textArrayResId requires the ID of an array resource that stores strings (such as "Degrees Celsius to Degrees Fahrenheit"), which happen to identify different kinds of conversions. The R.array.conversions argument passed to this parameter identifies conversions as the name of an array resource containing conversion strings and specified in a file named arrays.xml (described later in this recipe).

  • textViewResId requires the ID of the layout resource used to create the spinner's look. The android.R.layout.simple_spinner_item argument passed to this parameter is a predefined ID stored in the android package's R class's nested layout class. simple_spinner_item describes a spinner that looks something like a Java Swing combobox.

After calling createFromResource(Context, int, int), onCreate(Bundle) calls ArrayAdapter's void setDropDownViewResource(int resource) method with android.R.layout.simple_spinner_item as the argument. This method call creates the dropdown view portion of the spinner.

Now that the array adapter has been created and initialized with the appropriate unit conversion strings and layout information, onCreate(Bundle) attaches this information to the spinner by calling spnConversions.setAdapter(aa);. This method call allows the spinner widget to access this information and present a list of conversions to the user.

Note

Spinner inherits the void setAdapter(T) method from its abstract android.widget.AdapterView<T extends Adapter> ancestor class.

UC needs to keep track of the currently selected spinner item so that it can perform the appropriate conversion. onCreate(Bundle) makes this possible by registering a listener with the spinner that responds to item-selected events by assigning the spinner's position to the (previously mentioned) position variable.

onCreate(Bundle) first instantiates an anonymous class that implements ArrayAdapter's nested OnItemSelectedListener interface, and then registers this instance with the spinner by calling AdapterView's void setOnItemSelectedListener(AdapterView.OnItemSelectedListener listener) method.

OnItemSelectedListener's void onItemSelected(AdapterView<?> parent, View view, int position, long id) method is invoked whenever the user selects a new item, and is the perfect place to save the position. Although not needed, the companion void onNothingSelected(AdapterView<?> parent) method must also be implemented.

With the spinner out of the way, onCreate(Bundle) turns its attention to creating the Clear, Convert, and Close buttons. For each button, it invokes findByViewId(int) to obtain the button information from main.xml, and then instantiate the android.widget.Button class.

AdapterView's nested onClickListener interface is then employed to create listener objects, whose void onClick(View v) methods are invoked whenever the user clicks a button. Each listener is registered with its Button object by calling AdapterView's void setOnItemClickListener(AdapterView.OnItemClickListener listener) method.

The Clear button's click listener simply executes etUnits.setText("") to clear user input or a conversion result from the etUnits textfield. The Close button's click listener is equally simple; it invokes finish() to terminate the current activity and UC app. In contrast, the Convert button's click listener has more work to accomplish:

  1. Obtain the contents of the etUnits textfield as a String object: String text = etUnits.getText().toString();.

  2. Parse this String object into a double precision floating-point value: double input = Double.parseDouble(text);.

  3. Perform the conversion and save the result based on position's value: result = input*9.0/5.0+32;, result = (input-32)*5.0/9.0;, or result = input*multipliers[position];.

  4. Update etUnits with the result: etUnits.setText(""+result);.

There's one more task for onCreate(Bundle) to perform: make sure that the Clear and Convert buttons are disabled when etUnits is empty. After all, there's no point clearing an empty textfield, and parseDouble() throws an exception when attempting to parse an empty textfield.

onCreate(Bundle) accomplishes this task by registering a textwatcher (an object whose class implements the android.text.TextWatcher interface) with the etUnits textfield, via android.widget.TextView's void addTextChangedListener(TextWatcher watcher) method. TextView is EditText's superclass.

TextWatcher declares void afterTextChanged(Editable s), void beforeTextChanged(CharSequence s, int start, int count, int after), and void onTextChanged(CharSequence s, int start, int before, int count) methods. Only the latter method is overridden to enable or disable the Clear and Convert buttons.

onTextChanged(s, int, int, int) first evaluates etUnits.getText().length(), which returns the textfield's length. If the length is 0 (empty textfield), the buttons are disabled via btnClear.setEnabled(false); and btnConvert.setEnabled(false);. Otherwise, they're enabled via btnClear.setEnabled(true); and btnConvert.setEnabled(true);.

Most of UC's resources are stored in XML files. For example, UC's widget and layout information is stored in main.xml, which Listing 1-10 presents.

Example 1.10. The main.xml File Storing Widget and Layout Information

<?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"
   android:gravity="center_vertical"
   android:background="@drawable/gradientbg"
   android:padding="5dip">
   <LinearLayout android:layout_width="fill_parent"
      android:layout_height="wrap_content">
      <TextView android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_marginRight="10dip"
         android:text="@string/units"
         android:textColor="#000000"
         android:textSize="15sp"
         android:textStyle="bold"/>
      <EditText android:id="@+id/units"
         android:layout_width="fill_parent"
         android:layout_height="wrap_content"
         android:hint="type a number"
         android:inputType="numberDecimal|numberSigned"
         android:maxLines="1"/>
   </LinearLayout>
<Spinner android:id="@+id/conversions"
      android:layout_width="fill_parent"
      android:layout_height="wrap_content"
      android:prompt="@string/prompt"/>
   <LinearLayout android:layout_width="fill_parent"
      android:layout_height="wrap_content">
      <Button android:id="@+id/clear"
         android:layout_width="fill_parent"
         android:layout_height="wrap_content"
         android:layout_weight="1"
         android:text="@string/clear"/>
      <Button android:id="@+id/convert"
         android:layout_width="fill_parent"
         android:layout_height="wrap_content"
         android:layout_weight="1"
         android:text="@string/convert"/>
      <Button android:id="@+id/close"
         android:layout_width="fill_parent"
         android:layout_height="wrap_content"
         android:layout_weight="1"
         android:text="@string/close"/>
   </LinearLayout>
</LinearLayout>

Listing 1-10 begins by declaring a <LinearLayout> tag that specifies a layout (a view group that arranges contained views on an Android device's screen in some manner) for arranging contained widgets and nested layouts either horizontally or vertically across the screen.

The <LinearLayout> tag specifies several attributes for controlling this linear layout. These attributes include the following:

  • orientation identifies the linear layout as horizontal or vertical. The default orientation is horizontal. "horizontal" and "vertical" are the only legal values that can be assigned to this attribute.

  • layout_width identifies the width of the layout. Legal values include "fill_parent" (occupy the entire width) and "wrap_content" (occupy only the width required by the view). fill_parent was renamed to match_parent in Android 2.2, but is still supported and widely used.

  • layout_height identifies the height of the layout. Legal values include "fill_parent" (occupy the entire height) and "wrap_content" (occupy only the height required by the view). fill_parent was renamed to match_parent in Android 2.2, but is still supported and widely used.

  • gravity identifies how the layout is positioned relative to the screen. For example, "center_vertical" specifies that the layout should be centered vertically on the screen.

  • background identifies a background image or gradient via a resource reference (special syntax beginning with the @ character). For example, "@drawable/gradientbg" references a drawable resource (an image or a graphic) named gradientbg.

  • padding identifies space to add to the layout to provide a boundary between itself and the screen's edges. "5dip" refers to five density-independent pixels, virtual pixel units that apps can use to express layout dimensions/positions in a screen density-independent way.

Note

A density-independent pixel is equivalent to one physical pixel on a 160-dpi screen, the baseline density assumed by Android. At run time, Android transparently handles any scaling of the required dip units, based on the actual density of the screen in use. Dip units are converted to screen pixels via equation pixels = dips * (density / 160). For example, on a 240-dpi screen, 1 dip equals 1.5 physical pixels. Google recommends using dip units to define your app's user interface to ensure proper display of the UI on different screens.

A second linear layout has been nested inside the first linear layout. Because no orientation attribute is specified, this layout lays out its widgets horizontally. As with the parent layout, layout_width is assigned "fill_parent". However, layout_height is assigned "wrap_content" to prevent this nested layout from occupying the entire screen.

The nested linear layout encapsulates textview and edittext elements. The textview element describes a widget that serves as a label for the widget described by the edittext element. The <textview> tag presents the following attributes in addition to layout_width and layout_height:

  • layout_marginRight specifies the amount of space to reserve on the right side of the textview widget; 10 density-independent pixels have been selected as the space amount.

  • text identifies the text that this widget displays. The text is identified via @string/units, a string resource reference to the units entry in the standard strings.xml resource file (see Listing 1-12). This entry's value is the text.

  • textColor identifies the color of the text. The color is specified in #RRGGBB format – #00000 identifies black.

  • textSize identifies the text's size. The size is specified as "15sp", which is interpreted as 15 scale-independent pixels (the user selects the scaling via a device setting). Google recommends specifying scale-independent pixels (to let the user scale text) or device-independent pixels (to prevent the user from scaling text).

  • textStyle identifies the text styling, such as bold or italic. The style is set to "bold" to emphasize the text so that it stands out on the screen.

The <edittext> tag provides the following attributes:

  • id identifies this widget element so that it can be referenced from code. The resource identifier is specifed by using a special syntax that begins with the @+id prefix. For example, "@+id/units" identifies this edittext widget as units; this widget resource is referenced from code by specifying R.id.units.

  • hint identifies a string that appears in the textfield when nothing has been entered. It serves as a hint to the user about what kind of data to enter into the textfield. Instead of assigning a string resource reference to this attribute, the "type a number" literal string was assigned to make the following point: although you can embed literal string values in the resources (or even code), you really should store them in the separate strings.xml resource file to facilitate localization of the app to a different language, such as French or German.

  • inputType identifies the kind of data that you want the user to enter. By default, any character can be entered. Because this is unacceptable when a number is required, "numberDecimal|numberSigned" is assigned to inputType. This string specifies that only decimal numbers can be entered. Furthermore, these numbers can be negative.

  • maxLines restricts the number of lines of text that can be entered into a textfield. The "1" assignment indicates that only a single line of text can be entered.

Below the linear layout element lies a spinner element named conversions. This element is declared to fill the screen's width, but not the screen's height. Futhermore, its prompt attribute is assigned "@string/prompt" to prompt the user (on the dropdown view, which is shown in Figure 1-15) to select a conversion.

Below the spinner element lies another nested linear layout, encapsulating the Clear, Convert, and Close buttons. Each button is assigned a unique ID so it can be referenced from code. Its layout_weight attribute is assigned the same value as the other buttons' layout_weight attributes so that each button has the same width (it looks nicer).

Android let you declare shape resources (such as rectangles or ovals) as XML files. These shapes can be declared with straight or rounded corners, with gradient backgrounds, and with other attributes. For example, Listing 1-11 introduces a rectangle shape with a gradient background.

Example 1.11. The gradientbg.xml File Storing a Gradient Shape to Color the Activity's Background

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
   <gradient android:startColor="#fccb06"
      android:endColor="#fd6006"
      android:angle="270"/>
   <corners android:radius="10dp"/>
</shape>

The <shape> tag introduces a shape via its shape attribute. If this attribute is not present, the shape defaults to a rectangle.

The nested <gradient> tag defines the shape's color in terms of a gradient, which is specified via startColor, endColor, and angle attributes. The angle attribute specifies the direction that the gradient sweeps across the rectangle. If angle is not present, the angle defaults to 0 degrees.

The nested <corners> tag determines whether or not a rectangle shape has corners. If this tag is present, its attributes identify the degree of roundness for each or all corners. For example, the radius attribute in Listing 1-11 specifies that each corner has a radius of 10 density-independent pixels – dp is a synonym for dip.

Strings should be stored separately to facilitate localization of text. Android mandates that strings be stored in a file named strings.xml, which Listing 1-12 presents.

Example 1.12. The strings.xml File Storing the App's Strings

<?xml version="1.0" encoding="utf-8"?>
<resources>
   <string name="app_name">Units Converter</string>
   <string name="clear">Clear</string>
   <string name="close">Close</string>
   <string name="convert">Convert</string>
   <string name="prompt">Select a conversion</string>
   <string name="units">Units</string>
</resources>

The strings.xml file stores its strings as a sequence of string elements that are nested in a resources element. Each <string> tag requires a unique name attribute whose content identifies the string, and which is referenced from code or some other resource. The string text is placed between the <string> and </string> tags.

Finally, the array of conversion strings is stored in arrays.xml. Listing 1-13 reveals this standard file's contents.

Example 1.13. The arrays.xml File Storing an Array of Conversion Strings

<?xml version="1.0" encoding="utf-8"?>
<resources>
   <string-array name="conversions">
      <item>Acres to Square Miles</item>
      <item>Atmospheres to Pascals</item>
      <item>Bars to Pascals</item>
      <item>Degrees Celsius to Degrees Fahrenheit</item>
      <item>Degrees Fahrenheit to Degrees Celsius</item>
      <item>Dynes to Newtons</item>
      <item>Feet/Second to Metres/Second</item>
      <item>Fluid Ounces (UK) to Litres</item>
      <item>Fluid Ounces (US) to Litres</item>
      <item>Horsepower (electric) to Watts</item>
      <item>Horsepower (metric) to Watts</item>
      <item>Kilograms to Tons (UK or long)</item>
      <item>Kilograms to Tons (US or short)</item>
      <item>Litres to Fluid ounces (UK)</item>
      <item>Litres to Fluid ounces (US)</item>
<item>Mach Number to Metres/Second</item>
      <item>Metres/Second to Feet/Second</item>
      <item>Metres/Second to Mach Number</item>
      <item>Miles/Gallon (UK) to Miles/Gallon (US)</item>
      <item>Miles/Gallon (US) to Miles/Gallon (UK)</item>
      <item>Newtons to Dynes</item>
      <item>Pascals to Atmospheres</item>
      <item>Pascals to Bars</item>
      <item>Square Miles to Acres</item>
      <item>Tons (UK or long) to Kilograms</item>
      <item>Tons (US or short) to Kilograms</item>
      <item>Watts to Horsepower (electric)</item>
      <item>Watts to Horsepower (metric)</item>
   </string-array>
</resources>

Android lets you store arrays with different types of data in arrays.xml. For example, <string-array> indicates that the array contains strings. This tag requires a name attribute whose value uniquely identifies this array. Each array item is specified by placing its content between <item> and </item> tags.

1-6. Creating UC

Problem

You want to learn how to create UC using the Android SDK's command-line tools, but are not sure how to accomplish this task.

Solution

Use the android tool to create UC, and then use ant to build this project.

How It Works

Your first step in creating UC is to use the android tool to create a project. When used in this way, android requires you to adhere to the following syntax (which is spread across multiple lines for readability):

android create project --target target_ID
                       --name your_project_name
                       --path /path/to/your/project/project_name
                       --activity your_activity_name
                       --package your_package_namespace

Except for --name (or –n), which specifies the project's name (if provided, this name will be used for the resulting .apk filename when you build your app), all of the following options are required:

  • The --target (or -t) option specifies the app's build target. The target_ID value is an integer value that identifies an Android platform. You can obtain this value by invoking android list targets. If you've only installed the Android 2.3 platform, this command should output a single Android 2.3 platform target identified as integer ID 1.

  • The --path (or -p) option specifies the project directory's location. The directory is created if it doesn't exist.

  • The --activity (or -a) option specifies the name for the default activity class. The resulting classfile is created inside /path/to/your/project/project_name/src/your_package_namespace/, and is used as the .apk filename if --name (or -n) isn't specified.

  • The --package (or -k) option specifies the project's package namespace, which must follow the rules for packages that are specified in the Java language.

Assuming a Windows XP platform, and assuming a C:prjdev hierarchy where the UC project is to be stored in C:prjdevUC, invoke the following command from anywhere in the filesystem to create UC:

android create project -t 1 -p C:prjdevUC -a UC -k com.apress.uc

This command creates various directories and adds files to some of these directories. It specifically creates the following file and directory structure within C:prjdevUC:

  • AndroidManifest.xml is the manifest file for the app being built. This file is synchronized to the Activity subclass previously specified via the --activity or -a option.

  • bin is the output directory for the Apache Ant build script.

  • build.properties is a customizable properties file for the build system. You can edit this file to override default build settings used by Apache Ant, and provide a pointer to your keystore and key alias so that the build tools can sign your app when built in release mode (discussed in Recipe 1-8).

  • build.xml is the Apache Ant build script for this project.

  • default.properties is the default properties file for the build system. Don't modify this file.

  • libs contains private libraries, when required.

  • local.properties contains the location of the Android SDK home directory.

  • proguard.cfg contains configuration data for ProGuard, an SDK tool that lets developers obfuscate their code (making it very difficult to reverse engineer the code) as an integrated part of a release build.

  • res contains project resources.

  • src contains the project's source code.

res contains the following directories:

  • drawable-hdpi contains drawable resources (such as icons) for high-density screens.

  • drawable-ldpi contains drawable resources for low-density screens.

  • drawable-mdpi contains drawable resources for medium-density screens. The gradientbg.xml file in Listing 1-11 is stored in this directory.

  • layout contains layout files. The main.xml file in Listing 1-10 is stored in this directory.

  • values contains value files. Listing 1-12's strings.xml and Listing 1-13's arrays.xml files are stored in this directory.

Also, src contains the comapressuc directory structure, and the final uc subdirectory contains a skeletal UC.java source file. This skeletal file's contents are replaced with Listing 1-9.

Assuming that C:prjdevUC is current, build this app with the help of Apache's ant tool, which defaults to processing this directory's build.xml file. At the command line, specify ant followed by debug or release to indicate the build mode:

  • Debug mode: Build the app for testing and debugging. The build tools sign the resulting APK with a debug key and optimize the APK with zipalign. Specify ant debug.

  • Release mode: Build the app for release to users. You must sign the resulting APK with your private key, and then optimize the APK with zipalign. (I discuss these tasks later in this chapter.) Specify ant release.

Build UC in debug mode by invoking ant debug from the C:prjdevUC directory. This command creates a gen subdirectory containing the ant-generated R.java file (in a comapressuc directory hierarchy), and stores the created UC-debug.apk file in the bin subdirectory.

1-7. Installing and Running UC

Problem

You want to install the UC-debug.apk package file that you just created on the previously started AVD and run this app.

Solution

Use the adb tool to install UC. Navigate to the app launcher screen to run UC.

How It Works

Assuming that the AVD is still running, execute adb install C:prjdevUCinUC-debug.apk to install UC-debug.apk on the AVD. After a few moments, you should see several messages similar to the following:

411 KB/s (19770 bytes in 0.046s)
        pkg: /data/local/tmp/UC-debug.apk
Success

From the home screen, click the app launcher icon (the rectangular grid icon centered at the bottom of the home screen) and scroll down on the result screen's list of app icons. Figure 1-12 shows you the Units Converter app entry.

The highlighted Units Converter app entry displays a custom icon (in an icon.png file, which is included in this book's code) that's also stored in drawable-mdpi.

Figure 1.12. The highlighted Units Converter app entry displays a custom icon (in an icon.png file, which is included in this book's code) that's also stored in drawable-mdpi.

Click the Units Converter icon and you should see the screen shown in Figure 1-13.

The Units textfield prompts the user to type a number.

Figure 1.13. The Units textfield prompts the user to type a number.

Enter 37 into the Units textfield and you'll see the screen shown in Figure 1-14.

The Clear and Convert buttons are no longer disabled.

Figure 1.14. The Clear and Convert buttons are no longer disabled.

Click the spinner and you'll see the screen shown in Figure 1-15.

The spinner displays the prompt at the top of its drop-down list of conversion names.

Figure 1.15. The spinner displays the prompt at the top of its drop-down list of conversion names.

Select "Degrees Celsius to Degrees Fahrenheit" and you'll see a screen similar to Figure 1-16.

The Units textfield displays the conversion result after clicking Convert.

Figure 1.16. The Units textfield displays the conversion result after clicking Convert.

Click Close to terminate the app and return to the launcher screen shown in Figure 1-12.

Note

Although UC appears to run correctly, its (and any other app's) code should be unit tested to verify that the code is correct before publishing the app. Google's online Android Developer's Guide delves into this topic in its "Testing" section at http://developer.android.com/guide/topics/testing/index.html.

1-8. Preparing UC for Publishing

Problem

You're satisfied that UC works properly, and now you want to prepare it for publishing to Google's Android Market or another publishing service.

Solution

Before you can publish an app such as UC, you should version the app. You then build the app in release mode, and sign and align its app package.

How It Works

Google's online Android Developer's Guide (http://developer.android.com/guide/index.html) provides extensive information on publishing an app. Rather than repeat the guide's information, this recipe presents the steps that are necessary to prepare UC for publishing.

Version UC

Android lets you add version information to your app by specifying this information in AndroidManifest.xml's <manifest> tag via its versionCode and versionName attributes.

versionCode is assigned an integer value that represents the version of the app's code. The value is an integer so that other apps can programmatically evaluate it to check an upgrade or downgrade relationship, for example. Although you can set the value to any desired integer, you should ensure that each successive release of your app uses a greater value. Android doesn't enforce this behavior, but increasing the value in successive releases is normative.

versionName is assigned a string value that represents the release version of the app's code, and should be shown to users (by the app). This value is a string so that you can describe the app version as a <major>.<minor>.<point> string, or as any other type of absolute or relative version identifier. As with android:versionCode, Android doesn't use this value for any internal purpose. Publishing services may extract the versionName value for display to users.

The <manifest> tag in UC's AndroidManifest.xml file includes a versionCode attribute initialized to "1" and a versionName attribute initialized to "1.0".

Build UC in Release Mode

Assuming Windows XP, the previous C:prjdevUC directory, and that this directory is current, execute the following command line:

ant release

This command line generates UC-unsigned.apk and stores this file in the bin directory. It also outputs a message stating that this APK must be signed and aligned with zipalign.

Sign UC's App Package

Android requires that all installed apps be digitally signed with a certificate whose private key is held by the app's developer. Android uses the certificate as a means of identifying the app's author and establishing trust relationships between apps; it doesn't use the certificate to control which apps can be installed by the user. Certificates don't need to be signed by certificate authorities: it's perfectly allowable, and typical, for Android apps to use self-signed certificates.

Note

Android tests a signer certificate's expiration date only at install time. If an app's signer certificate expires after the app is installed, the app will continue to function normally.

Before you can sign UC-unsigned.apk, you must obtain a suitable private key. A private key is suitable if it meets the following criteria:

  • The key represents the personal, corporate, or organizational entity to be identified with the app.

  • The key has a validity period that exceeds the expected lifespan of the app. Google recommends a validity period of more than 25 years. If you plan to publish the app on Android Market, keep in mind that a validity period ending after October 22, 2033 is a requirement. You cannot upload an app if it's signed with a key whose validity expires before that date.

  • The key is not the debug key generated by the Android SDK tools.

The JDK's keytool tool is used to create a suitable private key. The following command line (split over two lines for readability) uses keytool to generate this key:

keytool -genkey -v -keystore uc-release-key.keystore -alias uc_key -keyalg RSA
        -keysize 2048 -validity 10000

The following command-line arguments are specified:

  • -genkey causes keytool to generate a public and a private key (a key pair).

  • -v enables verbose output.

  • -keystore identifies the keystore (a file) that stores the private key; the keystore is named uc-release-key.keystore in the command line.

  • -alias identifies an alias for the key (only the first eight characters are used when the alias is specified during the actual signing operation); the alias is named uc_key in the command line.

  • -keyalg specifies the encryption algorithm to use when generating the key; although DSA and RSA are supported, RSA is specified in the command line.

  • -keysize specifies the size of each generated key (in bits); 2048 is specified in the command line because Google recommends using a key size of 2048 bits or higher (the default size is 1024 bits).

  • -validity specifies the period (in days) in which the key remains valid (Google recommends a value of 10000 or greater); 10000 is specified in the command line.

keytool prompts you for a password (to protect access to the keystore), and to reenter the same password. It then prompts for your first and last name, your organizational unit name, the name of your organization, the name of your city or locality, the name of your state or province, and a two-letter country code for your organizational unit.

keytool subsequently prompts you to indicate whether or not this information is correct (by typing yes and pressing Enter, or by pressing Enter for no). Assuming you entered yes, keytool lets you choose a different password for the key, or use the same password as that of the keystore.

Warning

Keep your private key secure. Fail to do so, and your app authoring identity and user trust could be compromised. Here are some tips for keeping your private key secure:

  • Select strong passwords for the keystore and key.

  • When you generate your key with keytool, don't supply the -storepass and -keypass options at the command line. If you do so, your passwords will be available in your shell history, which any user on your computer could access.

  • When signing your apps with jarsigner, don't supply the -storepass and -keypass options at the command line (for the same reason as mentioned in the previous tip).

  • Don't give or lend anyone your private key, and don't let unauthorized persons know your keystore and key passwords.

keytool creates uc-release-key.keystore in the current directory. You can view this keystore's information by executing the following command line:

keytool -list -v -keystore uc-release-key.keystore

After requesting the keystore password, keytool outputs the number of entries in the keystore (which should be one) and certificate information.

The JDK's jarsigner tool is used to sign UC-unsigned.apk. Assuming that C:prjdevUC is the current directory, this directory contains the keytool-created uc-release-key.keystore file, and this directory contains a bin subdirectory that contains UC-unsigned.apk, execute the following command line to sign this file:

jarsigner -verbose -keystore uc-release-key.keystore bin/UC-unsigned.apk uc_key

The following command-line arguments are specified:

  • -verbose enables verbose output.

  • -keystore identifies the keystore that stores the private key; uc-release-key.keystore is specified in the command line.

  • bin/UC-unsigned.apk identifies the location and name of the APK being signed.

  • uc-key identifies the previously created alias for the private key.

jarsigner prompts you to enter the keystore password that you previously specified via keytool. This tool then outputs messages similar to the following:

adding: META-INF/MANIFEST.MF
   adding: META-INF/UC_KEY.SF
   adding: META-INF/UC_KEY.RSA
  signing: res/layout/main.xml
  signing: AndroidManifest.xml
  signing: resources.arsc
  signing: res/drawable-hdpi/icon.png
  signing: res/drawable-ldpi/icon.png
  signing: res/drawable-mdpi/gradientbg.xml
  signing: res/drawable-mdpi/icon.png
  signing: classes.dex

Execute jarsigner -verify bin/UC-unsigned.apk to verify that UC-unsigned.apk has been signed.

Assuming success, you should notice a single "jar verified." message. Assuming failure, you should notice the following messages:

no manifest.
jar is unsigned. (signatures missing or not parsable)

Align UC's App Package

As a performance optimization, Android requires that a signed APK's uncompressed content be aligned relative to the start of the file, and supplies the zipalign SDK tool for this task. According to Google's documentation, all uncompressed data within the APK, such as images or raw files, is aligned on 4-byte boundaries.

zipalign requires the following syntax to align an input APK to an output APK:

zipalign [-f] [-v] <alignment>infile.apkoutfile.apk

The following command-line arguments are specified:

  • -f forces outfile.apk to be overwritten if it exists.

  • -v enables verbose output.

  • alignment specifies that the APK content is to be aligned on this number of bytes boundary; it appears that zipalign ignores any value other than 4.

  • infile.apk identifies the signed APK file to be aligned.

  • outfile.apk identifies the resulting signed and aligned APK file.

Assuming that C:prjdevUCin is the current directory, execute the following command line to align UC-unsigned.apk to UC.apk:

zipalign –f –v 4 UC-unsigned.apk UC.apk

zipalign requires the following syntax to verify that an existing APK is aligned:

zipalign -c -v <alignment>existing.apk

The following command-line arguments are specified:

  • -c confirms the alignment of existing.apk.

  • -v enables verbose output.

  • alignment specifies that the APK content is aligned on this number of bytes boundary; it appears that zipalign ignores any value other than 4.

  • infile.apk identifies the signed APK file to be aligned.

Execute the following command line to verify that UC.apk is aligned:

zipalign –c –v 4 UC.apk

zipalign presents a list of APK entries, indicating which are compressed and which are not, followed by a verification successful or a verification failed message.

1-9. Migrating to Eclipse

Problem

You prefer to develop apps using the Eclipse IDE.

Solution

To develop apps with Eclipse, you need to install an IDE such as Eclipse Classic 3.6.1. Furthermore, you need to install the ADT Plugin.

How It Works

Before you can develop Android apps with Eclipse, you must complete at least the first two of the following three tasks:

  1. Install the Android SDK and at least one Android platform (see Recipes 1-1 and 1-2). JDK 5 or JDK 6 must also be installed.

  2. Install a version of Eclipse that's compatible with the Android SDK and the Android Development Tools (ADT) Plugin for the Eclipse IDE.

  3. Install the ADT Plugin.

You should complete these tasks in the order presented. You cannot install the ADT Plugin before installing Eclipse, and you cannot configure or use the ADT Plugin before installing the Android SDK and at least one Android platform.

The Eclipse.org website makes available for download several IDE packages that meet different requirements. Google places several stipulations and recommendations on which IDE package you should download and install:

  • Install an Eclipse 3.4 (Ganymede) or greater IDE package.

  • Make sure that the Eclipse package being downloaded includes the Eclipse JDT (Java Development Tools) Plugin. Most packages include this plugin.

  • You should install one of the Eclipse Classic (versions 3.5.1 and higher), Eclipse IDE for Java Developers, or Eclipse IDE for Java EE Developers packages.

Complete the following steps to install Eclipse Classic 3.6.1:

  1. Point your browser to the Eclipse Classic 3.6.1 page at www.eclipse.org/downloads/packages/eclipse-classic-361/heliossr1.

  2. Select the appropriate distribution file by clicking one of the links in the Download Links box on the right side of this page. For example, you might click Windows 32-bit platform.

  3. Click a download link and save the distribution file to your harddrive. For example, you might save eclipse-SDK-3.6.1–win32.zip to your harddrive.

  4. Unarchive the distribution file and move the eclipse home directory to a convenient location. For example, you might move eclipse to your C:Program Files directory.

  5. You might also want to create a desktop shortcut to the eclipse application located in the eclipse home directory.

Complete the following steps to install the latest revision of the ADT Plugin:

  1. Start Eclipse.

  2. The first time you start Eclipse, you will discover a Workspace Launcher dialog box following the splash screen. You can use this dialog box to select a workspace folder in which to store your projects. You can also tell Eclipse to not display this dialog box on subsequent startups. Change or keep the default folder setting and click OK.

  3. Once Eclipse displays its main window, select Install New Software from the Help menu.

  4. Click the Add button on the resulting Install dialog box's Available Software pane.

  5. On the resulting Add Repository dialog box, enter a name for the remote site (for example, Android Plugin) in the Name field, and enter https://dl-ssl.google.com/android/eclipse/ into the Location field. Click OK.

  6. You should now see Developer Tools in the list that appears in the middle of the Install dialog box.

  7. Check the checkbox next to Developer Tools, which will automatically check the nested Android DDMS, Android Development Tools, and Android Hierarchy Viewer checkboxes. Click Next.

  8. The resulting Install Details pane lists Android DDMS, Android Development Tools, and Android Hierarchy Viewer. Click Next to read and accept the license agreement and install any dependencies, and then click Finish.

  9. An Installing Software dialog box appears and takes care of installation. If you encounter a Security Warning dialog box, click OK.

  10. Finally, Eclipse presents a Software Updates dialog box that prompts you to restart this IDE. Click the Restart Now button to restart.

Tip

If you have trouble acquiring the plugin in Step 5, try specifying http instead of https (https is preferred for security reasons) in the Location field.

To complete the installation of the ADT Plugin, you must configure this plugin by modifying the ADT preferences in Eclipse to point to the Android SDK home directory. Accomplish this task by completing the following steps:

  1. Select Preferences from the Window menu to open the Preferences panel. For Mac OS X, select Preferences from the Eclipse menu.

  2. Select Android from the left panel.

  3. Click the Browse button beside the SDK Location textfield and locate your downloaded SDK's home directory (such as C:android-sdk-windows, for example).

  4. Click Apply followed by OK.

Note

For more information on installing the ADT Plugin, along with helpful information in case of difficulty, please review the ADT Plugin for Eclipse page (http://developer.android.com/sdk/eclipse-adt.html) in Google's online Android Developer's Guide.

1-10. Developing UC with Eclipse

Problem

Now that you've installed Eclipse Classic 3.6.1 and the ADT Plugin, you want to learn how to use this IDE/Plugin to develop UC.

Solution

You first need to create an Android Eclipse project named UC. You then introduce various source files and drag resources to various directories. Finally, you execute UC by selecting Run from the menubar.

How It Works

The first task in developing UC with Eclipse is to create a new Android project. Complete the following steps to create this project:

  1. Start Eclipse if not running.

  2. Select New from the File menu, and select Project from the resulting popup menu.

  3. On the New Project dialog box, expand the Android node in the wizard tree, select the Android Project branch below this node, and click the Next button.

  4. On the resulting New Android Project dialog box, enter UC into the Project name textfield. This entered name identifies the folder in which the UC project is stored.

  5. Select the Create new project in workspace radio button if not selected.

  6. Under Build Target, select the checkbox of the appropriate Android target to be used as UC's build target. This target specifies which Android platform you'd like your application to be built against. Assuming that you've installed only the Android 2.3 platform, only this build target should appear and should already be checked.

  7. Under Properties, enter Units Converter into the Application name textfield. This human-readable title will appear on the Android device. Continuing, enter com.apress.uc into the Package name textfield. This value is the package namespace (following the same rules as for packages in the Java programming language) where all your source code will reside. Check the Create Activity checkbox if not checked and enter UC as the name of the app's starting activity in the textfield that appears beside this checkbox. The textfield is disabled when this checkbox is not checked. Finally, enter integer 9 into the Min SDK Version textfield to identify the minimum API Level required to properly run UC on the Android 2.3 platform.

  8. Click Finish.

Eclipse responds by creating a UC directory with the following subdirectories and files within your Eclipse workspace directory:

  • .settings: This directory contains an org.eclipse.jdt.core.prefs file that records project-specific settings.

  • assets: This directory is used to store an unstructured hierarchy of files. Anything stored in this directory can later be retrieved by an app via a raw byte stream.

  • bin: Your APK file is stored here.

  • gen: The generated R.java file is stored within a subdirectory structure that reflects the package hierarchy (such as comapressuc).

  • res: App resources are stored in various subdirectories.

  • src: App source code is stored according to a package hierarchy.

  • .classpath: This file stores the project's classpath information so that external libraries on which the project depends can be located.

  • .project: This file contains important project information such as the kind of project it is, what builders it contains, and what linked resources are attached to the project.

  • AndroidManifest.xml: This file contains UC's manifest information.

  • default.properties: This file contains project settings.

  • Proguard.cfg: This file contains ProGuard configuration data.

Close the Welcome tab. Eclipse presents the user interface that's shown in Figure 1-17.

Eclipse's user interface is organized around a menubar, a toolbar, several windows (such as Package Explorer and Outline), a statusbar, and a blank area that's reserved for editor windows.

Figure 1.17. Eclipse's user interface is organized around a menubar, a toolbar, several windows (such as Package Explorer and Outline), a statusbar, and a blank area that's reserved for editor windows.

This user interface is known as the workbench. The Package Explorer window appears on the left and presents an expandable list of nodes that identify the various projects in the current workspace and their components. Figure 1-17 reveals that UC is the only project in the workspace.

To learn how Eclipse organizes the UC project, click the + icon to the left of this node. Figure 1-18 reveals an expanded project hierarchy.

Additional + icons have been clicked to reveal more of UC's file organization.

Figure 1.18. Additional + icons have been clicked to reveal more of UC's file organization.

Double-click the UC.java node. Eclipse responds by presenting the UC.java window that's revealed in Figure 1-19.

UC.java reveals skeletal content.

Figure 1.19. UC.java reveals skeletal content.

Replace UC.java's skeletal content with Listing 1-9 and disregard the errors that Eclipse reports. You'll correct these errors later.

Complete the following steps to introduce the necessary resources to this project:

  1. Double-click the main.xml node. Eclipse presents a main.xml editor window in graphical layout mode.

  2. Click the main.xml tab below the window to switch to text mode. Replace window content with Listing 1-10.

  3. Double-click the strings.xml node. Eclipse presents a strings.xml editor window in resources mode.

  4. Click the strings.xml tab below the window to switch to text mode. Replace window content with Listing 1-12.

  5. Right-click the values node and select New followed by Other from the popup menus. A New dialog box appears.

  6. Expand the XML node in the wizards list, select XML File, and click Next. On the next pane, replace NewFile.xml with arrays.xml in the File Name field; click Finish.

  7. Eclipse presents an arrays.xml editor window in design mode. Click the Source tab below the window to switch to text mode. Replace window content with Listing 1-13.

  8. Right-click the drawable-mdpi node and select New followed by Other from the popup menus. A New dialog box appears.

  9. Expand the XML node in the wizards list, select XML File, and click Next. On the next pane, replace NewFile.xml with gradientbg.xml in the File Name field; click Finish.

  10. Eclipse presents a gradientbg.xml editor window in design mode. Click the Source tab below the window to switch to text mode. Replace window content with Listing 1-11.

  11. Right-click the icon.png node underneath drawable-mdpi. Select Delete from the popup menu and delete this node.

  12. Copy the icon.png file from this chapter's section in this book's code archive to the clipboard. Right-click drawable-mdpi and select Paste from the popup menu.

Select Run from the menubar, and select Run from the resulting dropdown menu. On the resulting Run As dialog box, select Android Application and click OK.

If all goes well, Eclipse launches the emulator tool with the test_AVD device, installs the UC app, and causes this app to start running (see Figure 1-13).

Note

Eclipse provides much more support for Android app development than can be covered in this recipe. For example, if you need to debug a failing Android app, you can start the Dalvik Debug Monitor Service by selecting Open Perspective from the Window menu, followed by Other from the popup menu, followed by DDMS from the Open Perspective dialog box. To learn about DDMS, check out J Beer's "How-to use Dalvik Debug Monitor Service (DDMS) Tool With Google Android" tutorial (www.brighthub.com/mobile/google-android/articles/25023.aspx) and James Sugrue's "Debugging Android: Using DDMS To Look Under The Hood" tutorial (http://java.dzone.com/articles/debugging-android-using-ddms).

For additional insight into developing Android apps via Eclipse/ADT Plugin, check out Lars Vogel's "Android Development Tutorial – Gingerbread" tutorial (www.vogella.de/articles/Android/article.html).

Summary

Android has excited many people who are developing (and even selling) apps for this platform. It's not too late to join in the fun, and this chapter showed you how by taking you on a rapid tour of key Android concepts and development tools.

You first learned that Android is a software stack for mobile devices, and that this stack consists of apps, middleware, and the Linux operating system. You then learned about Android's history, including the various SDK updates that have been made available.

You next encountered Android's layered architecture, which includes apps at the top; an application framework, C/C++ libraries, and the Dalvik virtual machine as middleware; and a modified version of the Linux kernel at the bottom.

Continuing, you encountered app architecture, which is based upon components (activities, services, broadcast receivers, and content providers) that communicate with each other by using intents, that are described by a manifest, and that are stored in an app package.

You then learned how to implement activities by subclassing the android.app.Activity class, services by subclassing the abstract android.app.Service class, broadcast receivers by subclassing the abstract android.content.BroadcastReceiver class, and content providers by subclassing the abstract android.content.ContentProvider class.

At this point, Chapter 1 moved away from this essential theory and focused on practical matters via a series of recipes. Initial recipes focused on installing the Android SDK and an Android platform, creating an AVD, and starting the emulator with this AVD.

The next batch of recipes introduced you to a sample Units Converter app. They also showed you how to create this app, install it on the emulator, run it from the emulator, and how to prepare a release version for publication to Google's Android Market.

Working with command-line tools in a command-line environment can be tedious. For this reason, the final two recipes focused on migrating to the Eclipse IDE, and showed you how to develop Units Converter in the context of this graphical environment.

While exploring the Units Converter app, you were introduced to some user interface concepts. Chapter 2 builds upon these concepts by presenting recipes that focus on various Android user interface technologies.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.188.216.249