Creating Android Applications: Develop
Creating Android Applications: Develop
Applications
DEVELOP AND DESIGN
Chris Haseman
Creating
Android
Applications
DEVELOP AND DESIGN
Chris Haseman
Creating Android Applications: Develop and Design
Chris Haseman
Peachpit Press
1249 Eighth Street
Berkeley, CA 94710
510/524-2178
510/524-2221 (fax)
Notice of Rights
All rights reserved. No part of this book may be reproduced or transmitted in any form by any means,
electronic, mechanical, photocopying, recording, or otherwise, without the prior written permission of the
publisher. For information on getting permission for reprints and excerpts, contact [email protected].
Notice of Liability
The information in this book is distributed on an “As Is” basis without warranty. While every precaution has
been taken in the preparation of the book, neither the author nor Peachpit shall have any liability to any
person or entity with respect to any loss or damage caused or alleged to be caused directly or indirectly by
the instructions contained in this book or by the computer software and hardware products described in it.
Trademarks
Android is a trademark of Google Inc., registered in the United States and other countries. Many of the
designations used by manufacturers and sellers to distinguish their products are claimed as trademarks.
Where those designations appear in this book, and Peachpit was aware of a trademark claim, the designa-
tions appear as requested by the owner of the trademark. All other product names and services identified
throughout this book are used in editorial fashion only and for the benefit of such companies with no
intention of infringement of the trademark. No such use, or the use of any trade name, is intended to convey
endorsement or other affiliation with this book.
ISBN-13: 978-0-321-78409-4
ISBN-10: 0-321-78409-x
987654321
Chris Haseman has been writing mobile software in various forms since 2003.
He was involved in several large-scale BREW projects, from MMS messaging to
Major League Baseball. More recently, he was an early Android engineer behind
the doubleTwist media player, and he is now the lead Android developer for the
website Tumblr. He’s a faculty member of General Assembly in NYC, where he
teaches Android development. He lives in Brooklyn, where he constantly debates
shaving his beard.
As always, I could spend more pages thanking people than are in the work itself.
Here are a few who stand out:
David and Susanne H for their support. Ellen Y. for believing so early that I
could do this. JBL for fixing my code. Robyn T. for her patience. Cliff C. for finding
me. Scout F. for her tolerance of my grammar. Sharon H. for her harassment IMs.
Dan C. for his backing. Edwin and Susan K. for their care. Thomas K. for his subtle
and quiet voice. Sparks for his humor. Cotton for “being there.” Lee for the place
to write. The teams at both Tumblr and doubleTwist for all their encouragement.
The Android team at Google for all their hard work. Most of all, Peachpit for giving
me the opportunity to write for you.
ACKNOWLEDGMENTS V
CONTENTS
Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xi
Welcome to Android . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xiii
CONTENTS VII
Checking Your Work . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120
Wrapping Up . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122
CONTENTS IX
The Sneaky Shortcut . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 213
That’s It! . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 213
Show Me the Map! . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 214
Getting the Library . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 214
Adding to the Manifest . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 214
Creating the MapActivity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215
Creating a MapView . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 216
Run, Baby, Run . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 217
Wrapping Up . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 219
Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 248
If you’ve got a burning idea for an application that you’re dying to share, or if you
recognize the power and possibilities of the Android platform, you’ve come to the
right place. This is a short book on an immense topic.
I don’t mean to alarm anyone right off the bat here, but let me be honest: Android
development is hard. Its architecture is dissimilar to that of many existing platforms
(especially other mobile SDKs), there are many traps for beginners to fall into, and the
documentation is frequently sparse at best. In exchange for its difficulty, however,
Google’s Android offers unprecedented power, control, and—yes—responsibility to
those who are brave enough to develop for it.
This is where my job comes in. I’m here to make the process of learning to write
amazing Android software as simple as possible.
Who am I to ask such things of you? I’ve been writing mobile software in a
professional capacity for more than eight years, and for three of those years, I’ve
been developing software for Android. I’ve written code that runs on millions of
handsets throughout the world. Also, I have a beard. We all know that people with
ample facial hair appear to be more authoritative on all subjects.
In return for making this learning process as easy as possible, I ask for a few things:
䊏 You have a computer. My third-grade teacher taught me never to take any-
thing for granted; maybe you don’t have a computer. If you don’t already have
a computer, you’ll need one—preferably a fast one, because the Android
emulator and Eclipse can use up a fair amount of resources quickly.
䊏 You’re fluent in Java. Notice that I say fluent, not expert. Because you’ll
be writing usable applications (rather than production libraries, at least to
start), I expect you to know the differences between classes and interfaces.
You should be able to handle threads and concurrency without batting an
eyelash. Further, the more you know about what happens under the hood
(in terms of object creation and garbage collection), the faster and better
your mobile applications will be.
Yes, you can get through the book and even put together rudimentary
applications without knowing much about the Java programming language.
INTRODUCTION XI
However, when you encounter problems—in both performance and pos-
sibilities—a weak foundation in the programming language may leave you
without a solution.
䊏 You have boundless patience and endless curiosity. Your interest in and
passion for Android will help you through the difficult subjects covered in
this book and let you glide through the easy ones.
Throughout this book, I focus on how to write features, debug problems, and
make interesting software. I hope that when you’ve finished the book, you’ll have
a firm grasp of the fundamentals of Android software development.
All right, that’s quite enough idle talking. Let’s get started.
Eclipse and the Android SDK are the two major tools you’ll use to follow along with the
examples in this book. There are, however, a few others you should be aware of that will
be very useful now and in your future work with Android. While you may not use all of
these tools until you’re getting ready to ship an application, it will be helpful to know
about them when the need arises.
THE TOOLS
Over the course of this book, you’ll work with several tools that will make your life
with Google’s Android much easier. Here they are in no particular order:
WELCOME TO ANDROID XV
1
GETTING STARTED
WITH ANDROID
The first step when building
ngg an Android appli-
applli-
cation is installing the tools
ls and the SDK. If you’ve already
built an Android application,
ion, congratulations are in order!
You can skip this chapter and move on to the fundamentals. For
those of you who haven’t, you’ll get through this busy work before
you can say “Open Handset Alliance” three times quickly.
3
DOWNLOADING
DEVELOPER SOFTWARE
First, you need to download a few software tools—namely, the Android SDK,
the Eclipse integrated development environment (IDE), and the Android plug-in
for Eclipse. There are many other tools a developer could use to make Android
applications, but I’ve found that this setup has the fewest hassles and will get you
up and running in the least amount of time.
ECLIPSE
For versions of Eclipse newer than 3.5, Google recommends that you get the classic
version of the IDE. Tap your way to www.eclipse.org/downloads and locate Eclipse
Classic. (This chapter has screenshots from 3.6.1; the latest is, however, 3.7.1.) Make
sure you get the right version for your system: 32-bit or 64-bit. Now get your twid-
dling thumbs ready and wait for the installer to come through. Assuming that you’re
not connecting through a telephone line that makes hissing noises, you should be
finished in just a few minutes.
In the meantime, I’ll entertain you with an opera about the nature of kittens . . .
wait no, no I won’t. You’re welcome to browse ahead in the book while you down-
load the required files.
JAVA
You’ll need to download and install Java on your system (depending on how much
development you’ve done before, you might already have it installed). I assume
you were already comfortable with Java before diving into this book; I’m also going
to assume you’re comfortable installing the JDK yourself.
At this point, the process becomes a little more complicated and the herd of cats
start to wander in different directions. Depending on which platform you’re run-
ning, you may have to skip ahead from time to time. If the title doesn’t look like
it applies to your operating system (OS), skip ahead until you find one that does.
Bear with me; you’ll be working on your first application in no time.
NOTE: For the duration of this book, I’m going to assume you’ll
be using the Eclipse IDE for the majority of your development. I’ll try
to include command-line methods as well as Eclipse screenshots for
all important commands and tasks in case you’re rocking the terminal
with Vim or Emacs.
INSTALLING ECLIPSE
Installing Eclipse, for the most part, is as simple as decompressing the file you’ve
downloaded and putting the application somewhere you’ll remember. I recommend
not launching Eclipse just yet. Wait until you’ve got the Android SDK squared
away (see the next section). You may want to make sure that you’ve got the latest
development tools in place.
1. Navigate to /User/yourUserName/.profile.
Now, when you open a new terminal, typing which android will return the
path where you installed your shiny new Android SDK. Keep this command in
mind—you’ll return to it in a minute.
This procedure will add an SDK Manager command to your Start menu.
This is the application you’ll work with to select the correct platforms in
the next section.
DOWNLOADING A PACKAGE
All right, you’ve got the SDK downloaded and in the right place. You’re not quite
there yet.
NOTE: If you’ve closed it, you can find the SDK Manager program
in your Start menu under Android SDK Tools.
3. Select as many versions of the SDK as you like from the panel on the right.
(At press time, there are still a few phones running 1.6.) At the very least,
you’ll probably want Gingerbread (2.3.3), which many phones are running.
You’ll need Honeycomb (for tablets) and Ice Cream Sandwich (the latest
and greatest) for the last chapter of the book. If you’re in a rush, just grab
2.3.3 for now (Figure 1.1).
4. In the resulting dialog, click Install x Packages, agree to Google’s terms (read
at your own risk), and away you go.
The Android SDK Manager should download and install the two required
platforms for you. So far, so good.
Fortunately, configuring Eclipse is consistent for Windows, Mac, and Linux. Fire up
Eclipse and specify where you want to locate your workspace. It can, theoretically,
be installed anywhere, but I always locate mine under ~/Documents/workspace
on my Mac. As long as you consistently use the same directory, you shouldn’t
encounter any problems.
1. From the Eclipse Help menu, select Install New Software (Figure 1.2).
4. Select them all and click Next, then click Next again.
5. Accept Google’s terms and conditions. Eclipse will download the appropri-
ate plug-in packages.
Before the download finishes, you might be warned that unsigned code is
about to be installed. This is to be expected. (Don’t freak out.)
6. Accept the unsigned code warning and allow the download to continue.
If everything you’ve done thus far is working, you should see an Android
option in the list on the left.
3. Click Android.
CONFIGURING ECLIPSE 9
FIGURE 1.4 Tell Eclipse where
to find the Android SDK.
4. In the SDK Location field, enter the location to which you installed the SDK.
Figure 1.4 shows what it looks like on my Mac.
5. Click Apply.
In the large white box (which previously displayed “No target available”), you
should now see a list of available SDK platforms.
If you’re not seeing the list, then something isn’t right. Head back to the “Down-
loading a Package” section and see what needs sorting out.
CREATING AN EMULATOR
Although I said you had only one more step before you could create a project, and
that is true, you still need to create an emulator on which to run the project. So
hang in, you’re almost there.
Or, if you’re a command-line junkie, run android in the shell (I’m going to
assume you were able to add it to your path).
This screen should look familiar, because you just used it to install one or
two application platforms. Now you’re back to make a new virtual device.
2. With the Android SDK Manager open, make sure the Virtual Devices tab is
selected and click New. A new emulator dialog will pop up.
3. In the Name field, give your emulator a name; it’s best to give it one that
helps distinguish it from any others. You will have collected several emula-
tors before publishing your first application.
4. From the Target drop-down menu, specify which SDK you want to target.
It’s simplest right now to start with Gingerbread (2.3.3), but everything will
still work on Ice Cream Sandwich (4.0).
5. In the SD Card field, select the Size radio button and enter a small size.
6. In the Skin section, select the Built-In radio button and choose Default
WVGA800 from the drop-down menu.
CONFIGURING ECLIPSE 11
7. Click Create AVD and do a little dance next to your desk (or don’t, it’s up
to you).
8. Select your new emulator and click the Start button to get it running. The
laborious process of spinning up a new instance of the virtual device will begin.
NOTE: Pro emulator tip: Once you start an instance of the emulator, you
don’t ever have to start it up again. Reinstalling the application does
not (as it does with many other systems) require you to spawn a new
instance of the emulator.
1. Find the USB cable that came with your phone, and plug it into your
computer.
2. On your home screen, press the menu bar and go to Settings > Applica-
tions > Development and enable USB debugging by selecting the check box.
3. If you’re on a Windows machine, you may need to install the general USB
drivers. You can find them at http://developer.android.com/sdk/win-usb.html.
4. If you’ve finished everything correctly, you should see a little bug icon in
the notification bar on your device. Your phone will work in exactly the
same way an emulator would.
Congratulations! If you’ve followed every step thus far, you have your very own
shiny emulator or connected device, your Android SDK is correctly installed, and
you’re ready to rock and roll. Take a minute to bask in your own glory and play
around with your new emulator (Figure 1.6) before moving on to the next section,
which is about creating applications.
CONFIGURING ECLIPSE 13
CREATING A NEW
ANDROID PROJECT
Google has provided a few helpful ways to create a new Android project.
2. Choose File > New > Project. You should see the New Project screen
(Figure 1.7).
3. Click Next, and Android’s friendly project creation wizard will start
(Figure 1.8).
Let’s go over what each field means to your project as you complete them.
NOTE: If you’re not seeing the Android folder, you’ll need to make
sure you’ve correctly installed the Android Eclipse plug-in. Head back
to “Configuring Eclipse” and see where things may have gone awry.
This is how Eclipse keeps track of your project. Further, it will create a folder
with this name and put all your project files into it. The project name will
not show up anywhere on the Android device once you install. The project
name is something that really only matters to Eclipse, so I tend to pick
descriptive names for projects.
5. In the Build Target section, select the version of Android you’re targeting.
6. In the Application Name field, enter the full name of your application.
This is what will show in the app drawer after you have installed your app.
7. In the Package Name field, enter the Java package where you will place
your first activity.
8. Select the Create Activity check box and enter a name for your new activity
in the text box.
This step creates a new class with this name, so Java class naming conven-
tions apply. In Chapter 2, you’ll learn more specifics about what activities
are and how they work.
If you prefer to work from the command line, you can simply enter the
following three commands and move on with your day:
䊏 android create project -n MyFantasticSimpleProject -t 9 -p
myProjectDirectory -k com.haseman.fantasticProjctPackage -a
NewActivity
䊏 cd myProjectDirectory
䊏 ant install
1. If your emulator isn’t running, fire it back up. You need to make sure the
IDE is in communication with the emulator; they frequently lose touch
with each other. If you’re using a device, make sure it’s showing up cor-
rectly here as well.
2. Open the DDMS perspective by choosing Window > Open Perspective > Other.
3. Open the DDMS perspective. Under the Devices tab, you should see an entry
for your emulator or device.
4. From the Run menu in Eclipse, choose “Run last launched” or Run. Eclipse
may ask you to confirm that the app is indeed an Android project.
Android will compile, package, install, and run the application on your
emulator or device. If you can see the app running on your phone, congrats!
You’ve now officially created an Android application.
If you’re sure your emulator is running, but it refuses to display in the list of devices,
you may need to restart the Android Debug Bridge (ADB). Doing this requires get-
ting into the terminal a little bit.
When you run the start command, you should see the following lines:
* daemon not running. starting it now on port 5037 *
* daemon started successfully *
4. Switch back to your DDMS perspective; you should see the virtual device
listed in the devices window.
5. Switch back to the Java perspective and, from the Run menu, select . . . wait
for it . . . Run.
6. Select Android. It may ask you which device you’d like to run your project on.
Eclipse may also want to know which device you’d like to run your project on.
7. If your emulator isn’t running, this will be your chance to start a new one.
Otherwise, select your Android Virtual Device that is already running and
click OK.
Switching back to the emulator should show something close to Figure 1.9.
Although it doesn’t do much, you’ve successfully created and run your first
Android application. As Confucius said, a journey of a thousand miles begins
with a single step.
WRAPPING UP
This chapter covered downloading, installing, configuring, creating, and running
Android applications. You now have the very basic tools that you’ll need to continue
with this book. Feel free, if you’re struggling with the topics in the later chapters,
to refer back to this chapter as needed.
WRAPPING UP 19
2
EXPLORING
THE APPLICATION
BASICS
I’m sure you’re ready to roll
ll up
u your sleeves and
an
nd
write more code. However,
er, there are a few topics in the
realm of theory and design
gn to cover in detail first. In this
chapter, we’ll cover the basics of some essential building blocks,
including the files, parts, and terms that make up a simple Android
application; the Activity class that controls a single screen; the
Intent class, Android’s powerful communications class; and the
Application singleton class that can be accessed from all your
components.
21
THE FILES
Any mobile application, in its most basic form, consists of a single screen that
launches by clicking an icon on the device’s main screen.
When the SDK creates a basic Android project, it also creates several files and
important directories.
As with any project, before you start building the structure it’s important to at
least take a quick look over the blueprints. Here are the file and folders that make
up your Android project structure.
䊏 AndroidManifest.xml
䊏 /res
䊏 /src
Throughout the rest of this chapter, I’ll refer to the manifest and these folders.
THE MANIFEST
The AndroidManifest.xml file is your portal to the rest of the phone. In it, you’ll
describe which of your components should receive what events. You’ll also
declare, in the manifest file, what hardware and software your app will need
permission to access. First, let’s take a look at the <manifest> declaration in the
AndroidManifest.xml file:
<manifest xmlns:android=”http://schemas.android.com/apk/res/android”
package=”com.haseman.peachPit”
android:versionCode=”1”
android:versionName=”1.0”>
There are a few noteworthy items in this code. The package definition tells
Android in which Java package to look for the class files that make up the compo-
nents of your application. The next two variables are not particularly important right
now, but they will become vital once you’re ready to ship your application to the
Android Market. The versionCode is the number that helps the Market alert users
that an update is available. The versionName is a string that the application menus
and Market display to the user as the current version of your app.
Your application can have only one AndroidManifest.xml file. Henceforth, I’ll
refer to this file and concept simply as the manifest.
In a typical Android application, activities are the backbone of the operation. Essen-
tially, their purpose is to control what is displayed on the screen. They bridge the
gap between the data you wish to display and the UI layout files and classes that
do the work of displaying the data. If you’re familiar with the popular Model-View-
Controller (MVC) architecture, the activity would be the control for a screen. Here’s
what the activity declaration looks like in the manifest file:
<activity android:name=”.MyActivity”
android:label=”@string/app_name”>
<!-- More on how the intent-filter works in the next section-->
<intent-filter>
<action android:name=”android.intent.action.MAIN” />
<category android:name=”android.intent.category.LAUNCHER” />
</intent-filter>
</activity>
The android:name tag tells the system what to place on the end of your package
(from the manifest declaration) to find your class definition. For example, in my
sample project at com.haseman.peachPit.MyActivity, the class loader will look
to find a class that extends the Activity class.
In order to be found, the file must reside under the src/com/haseman/peachPit
directory. This is standard operating procedure for the language that Android uses.
3. Create a new file containing the XML layout instructions for this new activ-
ity, and add a new string literal for the layout to display (don’t worry, this
sounds a lot harder than it actually is).
4. When all the files are in place, you’ll need to actually launch this new activ-
ity from your existing one.
In this code, the device calls the onCreate method as the activity is starting.
onCreate tells the UI system that the setContentView method specifies the main
layout file for this activity. Each activity may have one and only one content view,
2. Add the following line inside the <application> tag and directly after the
</activity> closing tag of the previous declaration:
<activity android:name=”.NewActivity”/>
This little line tells the system where to find the new activity in your appli-
cation package. In the case of my demo, the class loader knows to look for
the activity at com.haseman.peachPit.NewActivity.
A name is enough to create a new file. The file will be saved in your main
package under the name you specified. In my demo, it is in my project under
src/com/haseman/peachPit/NewActivity.java.
Now that you have a class that extends an object, you’ll need to switch it
over to extend an activity.
Notice that this code looks very similar to what is already in your existing
activity. Let’s make it a little bit different.
In these lines, you told Android that you want a new string with the name
new_activity_text that can be accessed through Android’s resource man-
ager. You’ll learn much more about the contents of the /values folder in
later chapters. Next, you need to create a layout file for your new activity.
2. Insert the highlighted line to create a reference to the string you just created.
3. Give this modified TextView an ID so your Java code can reference it (you’ll
learn more about TextViews later; for now you should know that they’re
Android views that show text on the screen):
<?xml version=”1.0” encoding=”utf-8”?>
<LinearLayout xmlns:android=”http://schemas.android.com/apk/
p res/android”
android:orientation=”vertical”
android:layout_width=”fill_parent”
android:layout_height=”fill_parent”
>
<TextView
android:id=”@+id/new_activity_text_view”
android:layout_width=”fill_parent”
android:layout_height=”wrap_content”
android:text=”@string/new_activity_text”
/>
</LinearLayout>
I will devote Chapter 3 to resource management and user interface design, but
for now just keep in mind that the @ prefix is how you tell Android you want
to reference an ID, string, or drawable that is defined elsewhere as a resource.
Now that you have a new layout with its shiny new string, you’ll need to
tell the NewActivity class that you want it to use this particular layout file.
setContentView is the method in which you tell Android which XML file to
display for your new activity. Now that you’ve created a new class, string, and
layout file, it’s time to launch the activity and display your new view on the screen.
CATCHING KEYS
The simple way to launch your new activity is to have the user press the center key
on his or her device. If your phone doesn’t have a center key, you can easily modify
the following code to accept any key you like. To listen for key events, you’ll need to
extend the original activity’s onKeyDown method. Keep in mind that this is a simple
example case. Launching a new activity when the user presses a single key prob-
ably isn’t a very common use case in practice, but it makes for a good and simple
example. Most new activities are started when a user selects a list item, presses a
button, or takes another action with the screen.
Here’s what your new version of onKeyDown should look like:
@Override
public boolean onKeyDown(int keyCode, KeyEvent event){
if(keyCode == KeyEvent.KEYCODE_DPAD_CENTER){
//Launch new Activity here!
return true;
}
return super.onKeyDown(keyCode, event);
}
1. Place the following code into your onKeyDown key handling method:
Intent startIntent=new Intent(
this.getApplicationContext(),
NewActivity.class);
You’re passing the new intent an application context and the class object of
the activity that you would like to launch. This tells Android exactly where to
look in the application package. There are many ways to create and interact
with intents; this is, however, the simplest way to start up a new activity.
Once the intent is properly constructed, it’s simply a matter of telling the
Android system that you’d like to start the new activity.
NOTE: Throughout this whole process, the original activity has never
once had access to the instance of the new activity. Any information
that might pass between these two activities must go through the
intermediary intent. You’ll learn how this is done in the next section.
TRYING IT OUT
If you’re running Eclipse and you’ve been coding along with me, it should now be
a simple matter of spinning up the emulator and installing your new activity (the
steps for which you should remember from Chapter 1). Once your new application
has launched, press the center key to see the results of all your labor (Figure 2.1).
Now that you know how to create and launch a new activity, it’s time to discuss
how that process works. You’ll need to understand, for the purposes of UI layout
and data management/retention later, what methods are called each time one of
your activities makes its way onto, and off of, the screen.
You use them anytime you need to start an activity or service. Further, you’ll
frequently use intents for system-wide communication. For example, you
can receive notifications about power system changes by registering for a
widely published intent. If one of your activities registers for an intent in the
manifest (for example, com.haseman.peachPit.OhOhPickMe), then any applica-
tion anywhere on the phone can, if you make your activity public, launch
directly to your activity by calling
startActivity(new Intent(“com.haseman.peachPit.OhOhPickMe”));
1. onCreate
2. onStart
3. onResume
When you implement an activity, it’s your job to extend the methods that make
up this lifecycle. The only one you are required to extend is onCreate. The others,
if you declare them, will be called in the lifecycle order.
Your activity is the top visible application, can draw to the screen, will receive
key events, and is generally the life of the party. When the user presses the Back key
from the activity, these corresponding methods are called in the following order:
1. onPause
2. onStop
3. onDestroy
An activity’s life is an incredibly short one. They are constantly created and
destroyed, so it’s very important that you save no data in them that has any
relevance outside the screen your activity will control.
The following are all valid actions that will result in the destruction of
any activity:
1. The user rotated the device from portrait to landscape or vice versa.
3. It’s Tuesday.
4. The user has pressed the Back or Home buttons and left your application.
Again, make sure the data members of your activity pertain only to the ele-
ments on the screen. Further, don’t expect the activity to save any data for
you. You’ll learn strategies to handle this in later chapters.
After these methods have executed, your activity is closed down and should
be ready for garbage collection.
In order to make sense of the activity’s flow, let’s quickly look over each lifecycle
method in detail. Remember that you must evoke the superclass call in each of these
methods (often before you do anything else) or Android will throw exceptions at you.
NOTE: onCreate is the only one of the application lifecycle methods that
you must implement. I’ve found, in most of my work with Android,
that I only end up implementing one or two of these methods depend-
ing on what each activity is responsible for.
ONSTOP( )
When Android calls your onStop method, it indicates that your activity has officially
left the screen. Further, onStop is called when the user is leaving your activity
to interact with another one. This doesn’t necessarily mean that your activity is
shutting down (although it could). You can only assume that the user has left your
activity for another one. If you’re doing any ongoing process from within your
activity that should run only while it’s active, this method is your chance to be a
good citizen and shut it down.
ONDESTROY( )
onDestroy is your last method call before oblivion. This is your last chance for
your activity to clean up its affairs before it passes on to the great garbage collec-
tor in the sky.
Any background processes that your activity may have been running in the back-
ground (fetching/parsing data, for example) must be shut down on this method call.
However, just because onDestroy is called doesn’t mean that your activity will
be obliterated. So if you have a thread running, it may continue to run and take up
system resources even after the onDestroy method is called.
ONSAVEINSTANCESTATE(BUNDLE OUTSTATE)
This method passes you a bundle object into which you can put any data that you’d
need to restore your activity to its current state at a later time. You’ll do this by
calling something like outState.putString or outState.putBoolean. Each stored
value requires a string key going in, and it requires the same string key to come back
out. You are responsible for overriding your own onSaveInstanceState method.
If you’ve declared it, the system will call it; otherwise, you’ve missed your chance.
When your previously killed activity is restored, the system will call
your onCreate method again and hand back to you the bundle you built with
onSaveInstanceState.
onSaveInstanceState will only be called if the system thinks you may have to
restore your activity later. It wouldn’t be called if, for example, the user has pressed
the Back key, as the device clearly has no need to resume this exact activity later.
As such, this method is not the place for saving user data. Only stash temporary
information that is important to the UI on this particular instance of the screen.
ONRETAINNONCONFIGURATIONINSTANCE( )
When the user switches between portrait and landscape mode, your activity
is destroyed and a new instance of it is created (going through the full shut-
down-startup cycle of method calls). When your activity is destroyed and cre-
ated specifically because of a configuration change (the device rotation being
the most common), onRetainNonConfigurationInstance gives you a chance to
return any object that can be reclaimed in your new activity instance by calling
getLastNonConfigurationInstance.
This tactic helps to make screen rotation transitions faster. Keep this in mind
if it takes your activity a significant amount of time to acquire data that it plans
on displaying to the screen. Instead, you can get the previously displayed data by
using getLastNonConfigurationInstance.
By now you know that activities can be killed off with very little notice. The
onSaveInstanceState gives you a chance to save primitives for later use. This
means, unequivocally, that your entire activity must be able to collapse all its
important information into a series of primitives. This further reinforces the
notion that activities must be very simple and cannot contain any complex
data important to the application outside itself. Avoid keeping large Java col-
lections filled with data in your activity, as it may be terminated with very
little notice.
You have what you need to keep up as I go over more complex topics in later
chapters. Fear not, I’ll come back to the activity in no time.
An intent is a class. Intents, in the Android platform, make up the major commu-
nication protocol for moving information between application components. In
a well-designed Android application, components (activity, content provider, or
service) should never directly access an instance of any other component. As such,
intents are how these pieces are able to communicate.
A good half of this book could be dedicated to the creation, use, and details of
the Intent class. For the sake of brevity and getting you up and running as fast as
possible, I’ll cover only a few basics in this chapter. Look for intents throughout the
rest of this book. They’re probably the most often used class in Android as a whole.
There are two main ways to tell the Android system that you’d like to receive
intents sent out by the system, by other applications, or even by your own app:
䊏 Registering an <intent-filter> in the AndroidManifest.xml file
䊏 Registering an IntentFilter object at runtime with the system
In each case, you need to tell the Android system what events you want to
listen for.
There are huge heaping numbers of ways to send intents as well. You can broad-
cast them out to the system as a whole, or you can target them to a specific activity
or service. However, in order to start a service or activity, it must be registered in
the manifest (you saw an example of this in the previous demonstration on start-
ing a new activity).
Let’s take a look at how to use intents in practice.
MANIFEST REGISTRATION
Why not register everything at runtime? If an intent is declared as part of your
manifest, the system will start your component so that it will receive it. Registra-
tion at runtime presupposes that you are already running. For this reason, anytime
you want your application to awaken and take action based on an event, declare it
in your manifest. If it’s something your application should receive only while it’s
running, register an IntentFilter (it’s an intent-filter when declared in XML,
but an IntentFilter in your Java code) once your particular component has started.
<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>
ADDING AN INTENT
If you skipped the previous section about the Activity class, now may be a good
time to go back and at least skim over the code. In that section, I showed you how
to declare and launch a simple new activity. What I didn’t show you, however,
was how to make that activity accessible to the system as a whole by declaring an
<intent-filter> for it within your manifest. Let’s do that now.
Now, lest you think I’m a crazed children’s toy enthusiast, I’ve used this
rather absurdist action string to demonstrate a point—namely, that the
only requirement for the action string is that it be unique for your particular
component.
In the previous section, I showed you how to launch the new activity by
using the following lines:
Intent startIntent=new Intent(this, NewActivity.class);
startActivity(startIntent);
This works, but it has one major limitation: It cannot be launched out-
side your own application’s context. This renders useless one of the most
powerful features that the activity-intent model has to offer. Namely, any
application on the device, with the right intent, can use components within
your application.
Now that you’ve added the <intent-filter> to the sample project manifest,
you can launch this particular activity anywhere with the following code:
Intent actionStartIntent= new Intent
p (“com.haseman.PURPLE_PONY_POWER”);
startActivity(actionStartIntent);
Notice a very important difference between this code and the listing above it.
When you create the intent in this example, you’re not required to pass in a
Context object (the bundle of information that is required to communicate
with the system at large). This allows any application, with knowledge of
the required intents, to start the NewActivity class.
Now, when you press the down key in the sample application, you’ll see the
same activity launching using this new manifest-declared intent-filter.
If you’ve misspelled the intent’s action string or neglected to add the default
category to your intent-filter, you may get an android.content.Activity
NotFoundException.
This exception will be thrown by the startActivity method anytime you
create an intent that the system cannot connect to an activity listed in a manifest
on the device.
Registering for intent filters is not only the purview of the activity. Any Android
application component can register to be started when an intent action is broadcast
by the system.
One activity may register to receive any number of events. Typically, sending an intent is akin to telling the
activity “do this one thing!” That “one thing” can be anything from editing a file to displaying a list of pos-
sible files or actions. Again, as we’ve discussed, it’s important to limit the scope of your activity, so register-
ing for only one intent is often a good idea. However, because your activity could be registered for more
than one intent, it’s a good idea to call getIntent inside your onCreate method and check why you’re being
started so you can take the right action (by calling getAction).
CREATE A RECEIVER
Let’s add the runtime BroadcastReceiver to the MyActivity class. A Broadcast
Receiver is, as you can probably guess, an object with a single onReceive method.
Change the MyActivity class to look like the following:
A TOAST?
A toast is a short message shown to the user in a small pop-up along the
bottom of the screen. This is a reference, as far as I can tell, to the short
speech given before glasses are raised, tapped together, and then partaken
of. As always with toasts, the shorter and simpler the better.
1. In the onCreate method, create an intent filter and add the action Intent.
ACTION_AIRPLANE_MODE_CHANGED to it.
2. Add as many actions to this intent filter as you wish. When your receiver is
called, you’ll have to sort out which intent actually triggered the Broadcast
Receiver’s onReceive method by calling getAction() on the intent.
3. Test the code by holding the power button down; this will pop up a dialog
with several options.
4. Enable Airplane mode. If you’ve done everything right so far, you should
see a small message pop up along the bottom of the screen with your alert
message in it.
This is going to be the primary way in which your applications will listen for
information on the status of the system. Everything from the status of the battery
to the status of the Wi-Fi radio is at your fingertips with this tool. You can find out
more about what activities you can monitor, with what permissions, by looking
in the Android SDK documentation.
@Override
public void onDestroy(){
super.onDestroy();
unregisterReceiver(imageReceiver);
}
This notion of many activities registering for the same intent can have delightful
side effects. In Android, any application can register to share media with a given
MIME time by using the android.intent.action.SEND action.
Figure 2.3 is what the Share tab on my phone looks like when I press it in the
image gallery.
It is this ability to register for similar intents that allows seamless interaction as
each application registering this intent is given an entry on the Share menu. Click-
ing an entry in this list will start the registered activity and pass along as an extra
the location at which the image can be accessed. What is an extra? Good question.
You’re adding a string payload to the intent before using it to start an activity.
Whoever receives the intent will be able to pull this string out (assuming they know
it’s there) and use it as they see fit. Now that you know how to attach the data,
let’s take a look at an example of retrieving and using the string in NewActivity’s
onCreate method:
REVIEWING INTENTS
You’ve learned how to register for, create, and use the basic functionality of an
intent. As you now know, they can be registered for in the manifest or at runtime.
They can be sent by any application on the phone, and any number of application
components can register for the same intent.
The goal in this section was to get you started on the care and feeding of Android
intents. In future chapters and tasks, you’ll work with intents again in many dif-
ferent contexts.
<application android:icon=”@drawable/icon”
android:label=”@string/app_name”>
<!—Activities, Services, Broadcast Receivers, and Content Providers -->
</application>
Here you can see the <application> tag. This part of the manifest typically
contains information relevant to your application at large. android:icon tells the
system what icon to display in the main application list. android:label in this case
refers to another entry in the strings.xml file you were editing earlier.
2. Create a new class in your package that extends the Application class.
3. Profit!
Let’s go over steps 1 and 2 in depth. You’re on your own for step 3.
THE NAME
When it comes to the manifest, android:name refers not to the name of the object
being described, but to the location of the class in your Java package. The Application
declaration is no exception. Here’s what the opening tag of the application should
look like with the new declaration:
In this declaration, you tell the system what icon you want to represent your
application on the Android application drawer.
Once again, the class loader will look for your Application class by appending
the contents of android:name to the end of your package declaration within the
<manifest> opening tag. Now you’ll need to actually create this class to keep the class
loader from getting unhappy.
import android.app.Application;
public class SampleApplication extends Application{
public void onCreate(){
super.onCreate();
}
}
The Application can be a very simple class. It’s hard to understand what the
Application can do for you until you consider a few things:
That’s all there is to it. You can add public data members or context-sensitive
methods to your own version of the Application, and with one call all your com-
ponents will have access to the same object, like so:
Be sure that any data you put in the Application is relevant everywhere, because
the overhead for allocating and initializing the Application can become a drag
on startup times.
WRAPPING UP
Over the course of this chapter, I’ve exposed you to the fundamental building
blocks of an Android application. I used examples to get you started on
䊏 The manifest
䊏 Creating and using your own activities
䊏 Sending, receiving, and taking advantage of intents
䊏 Creating your own Application object
It’s my hope that through the rest of the book, you’ll be able to use the building
blocks you’ve learned in this chapter to understand how an Android application
functions. From here on out, I’ll be focusing more on how to do tasks rather than
on the theories that back them. On that note, let’s start making screens that include
more than just a single text view.
WRAPPING UP 51
3
CREATING USER
INTERFACES
Creating an intuitive, good-looking
d-lo
ooking interface iiss
one of the most importantt aspects
spects of a successful Android
Android
application. It doesn’t matter
atter how wonderful your code is
if the user cannot easily find and use the features you’ve worked
so hard to create. While Android’s diversity may give your appli-
cation access to a greater pool of devices, you must also contend
with making sure your application correctly supports all those
screen sizes.
53
THE VIEW CLASS
No, this isn’t a television show where several women debate the merits of com-
mon culture. The View class is the superclass for all the Android display objects.
Each and every user interface (UI) class from the simple ImageView to the mighty
RelativeLayout all subclass from this same object. In this section, you’ll learn the
basics of creating, adding, and modifying your existing layouts using Java code and
XML. You’ll also learn to create your own custom view. A view, at its very core, is
simply a rectangle into which you can display something. Subclasses take many
different forms, but they all need, simply, a space to show something to the user.
CREATING A VIEW
Creating a new view is something you’ve already done. In Chapter 1, you added
a view to an XML layout file as part of exploring how to launch a new activity. At
the time, I told you we’d get into the specifics of how these views were created and
modified, and, well, now’s the time! Let’s take a look at the default views generated
for you automatically when you create a new Android project.
VIEWS IN XML
Here’s what the default XML layout looks like in a new project:
For now, it’s important to note that every single layout must have a value set
for both its height and its width. Android may not fail to compile if you do
not specify these values (as they could be updated at runtime), but if it starts
to draw your views and the system height and width values are missing, then
your application will crash.
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
In this code, you’re telling the activity that you want it to inflate and render
the main layout for this activity. By inflating, I mean the conversion from the XML
in the project to a Java object that can be told to draw on the screen. You might be
thinking, “Is it possible to build and display a layout and view within Java alone?
Can I skip layout in XML entirely and go straight to the source?” Yes, you can, but
in practice you shouldn’t. Even so, understanding how to build a layout without
the XML will help you potentially modify any aspect of your layout at runtime.
You are always required to give a height and width for every view in your
hierarchy. You may use, for these definitions in particular, four different
values. They are:
䊏 wrap_content makes the view big enough to match what it contains. This
might mean different things to different views, but the concept remains
the same.
䊏 fill_parent and match_parent values actually mean exactly the same
thing. Namely, I want the dimensions of this view to match the dimen-
sions of my parent view.
䊏 dip or dp stands for device-independent pixels. This is a value that will
give you a consistent spacing regardless of the screen density (pixels per
inch) of the device. On the Nexus 1 or Nexus S, 1 dp ~= 1.5 pixels.
䊏 px stands for pixels. There are times when exact pixel values are necessary.
I advise against using this to declare screen locations, but it’s an option.
Let’s look at what is happening in the Java code. Instead of calling setContent
View on an ID from the R.java file, I’m passing it an instance that the LinearLayout
object returned from the buildLayouts method. In the buildLayouts method,
I’m first creating a new LinearLayout (passing in the application’s context) and a
new LayoutParams object. To match what’s in the XML, the new LayoutParam is
initialized with both the width and the height set to MATCH_PARENT. If you have
done Android development before, you should know that MATCH_PARENT is exactly
the same as the previously used FILL_PARENT.
Once I have the layout parameters initialized for the top LinearLayout, I can
pass them to the object with a setLayoutParams call. I’ve now got the LinearLayout
configured, so it’s time to move on to building the TextView.
This is a simple text view, so its layout parameters are very similar to those of
its parent’s layout. The only noticeable difference is that I’m setting the height,
when creating the layout parameters, to scale to fit the natural size of the TextView.
(Much more on dynamic heights and widths soon.)
Once I’ve told the TextView how it will be positioned in its parent layout via
the layout parameters, I tell it which string I’d like to display. This string is defined
in /res/values/strings.xml. The name attribute in XML determines what needs
<TextView
android:id=”@+id/text_holder”
android:layout_width=”fill_parent”
android:layout_height=”wrap_content”
android:text=”@string/hello”
/>
RETRIEVING A VIEW
Getting the existing instance of an Android view is as simple as calling findViewById
and passing in the ID value found in the R.java file. Given the earlier XML example,
here’s how you would grab an instance of the text view and modify its contents.
Depending on how dynamic your application is, views come and go fairly regu-
larly. Showing and hiding views is as simple as using the ever trusty findViewById
and then calling setVisibility on the returned object:
This code has three visibility settings. GONE and VISIBLE are the most obvious.
You’ll find yourself using INVISIBLE less often, however it’s great for keeping all
the other views inside your layouts from moving around when you want to hide
something.
There are two methods at work here. In the onCreate method that is called when
the activity is being initialized, you’ll see me pulling button_one and button_two
out of the layout with findViewById. If the system correctly returned an instance,
I register my activity (passing in a pointer to the activity with “this”) as the click
listener for each view.
Registering a click listener with a view does two things. First, it tells the system
to call the appropriate onClick method. Second, it tells the system that this view
accepts both focus (highlightable by the navigation buttons) and touch events. You
can switch these states on or off by yourself with code, but setting a click listener
ensures that click events can actually be heard by the view.
There’s a second way to set up the same dynamic. This method sets up a new
OnClickListener object for each view. This can help keep code separate if your
screen has a lot of clickable items on it. Here’s what this pattern looks like, and it
achieves the same results as the previous code.
EXTENDING A VIEW
Although Android’s layouts and views are powerful and versatile, there are times
when they just won’t do exactly what you want. Fortunately, their functionality is
easy to extend. To demonstrate, I’ve written a custom text view that changes the
color of every letter in the text to be displayed onscreen. While this isn’t the most
practical use case, it will show how simple it is to implement your own behavior.
I make sure I don’t get stuck in an infinite loop (with the change in color trig-
gering another onTextChanged call, which changes the color again, which changes
the color . . . you get the idea). Next comes the code that changes the colors:
There’s nothing you haven’t seen before going on here. I’m creating a new
instance of my view, setting the text for it, and then setting it as the main view for
my activity. You could also put it inside a layout object with other views. It’s also
possible to add this custom view to an XML-described layout. But before you can
start declaring your custom view in an XML file, you need to create the full suite
of View constructors. Your custom view should look something like this:
When Android parses your XML layout and creates your view, it needs to pass
an attribute set to the constructor because this contains all the layout informa-
tion, text, and whatever else you’ve added that starts with android. If you forget
to add these, everything will compile, but it will show the Unexpected Force Close
window of doom when you try to draw the screen.
Now that you have the correct constructors, it’s possible to create and lay out
your custom view within XML. In the code to follow, I’ve added a single instance
of the rainbow animating custom text display.
Android has many tools to help you manage string literals, images, layouts, and
more. Moving all this constant data into external files makes life as a programmer
easier in a multitude of ways. In previous code examples, I’ve referenced the R.java
file when specifying strings, drawable images, and layouts and mentioned that an
explanation would be forthcoming. Now is the time to explain Android resource
management in detail.
RESOURCE MANAGEMENT 71
Here’s what the R.java file looks like for a newly created project:
VALUES FOLDER
The prospect of putting all the constant values for your user interface (strings, colors,
or int/string arrays) in a separate file might sound annoying at first (especially when
you consider that all text manipulators will take a resource ID or a CharSequence).
However, it can cut down many days of work when translating your application
to different languages.
Having all your strings in an external XML file also means that your nontech-
nical colleagues (product managers, designers, or micromanaging bosses) can
manipulate the display text in the screens, menus, and pop-up dialogs without
bothering you. This assumes, of course, that you teach them how to compile and
run the project; feel free to share the first chapter of this book with them.
The values folder can contain:
Arrays. There is a file for the XML-defined arrays, but string arrays can still
go in the strings.xml file if you don’t feel like using a separate file.
RESOURCE MANAGEMENT 73
Colors. colors.xml can contain any number of declared color constants
for use in everything from text fonts to layout backgrounds. Unless you’re
planning on doing a lot of custom UI drawing, this file will probably not
be very large.
You can create new values folders for each language by using the two-letter
ISO639-2 suffix for a particular language as a suffix to values. You can, for example,
create a values-es folder containing a Spanish version of the strings.xml file.
When a user sets his or her phone to Spanish, Android will check automatically for
an R.string.hello value defined in the strings.xml file within the new values-es
folder. If it finds one, it will display the values-es version rather than the default.
If it doesn’t find a Spanish translation, it will default to the value you defined in
values/strings.xml.
In this way, Android provides you with an easy way to localize all the strings
in your application. It does, however, require you to be vigilant about placing your
string literals in the strings.xml file rather than just calling setText(“Text like
this”); or using android:text=”Text like this” in your XML.
LAYOUT FOLDERS
I’m going to give you three guesses as to what exactly goes into the layout folders.
Typically, it’s where you place either layout XML files for use in setContentView
calls, or sub-layouts that can be included via ViewStubs or inherited views (two
tools that allow you to reuse views in different layouts). Android builds a helpful
mechanic into the layout folders. You can have many folders, with different suf-
fixes, that describe how you want the application to appear under various screen
configurations.
RESOURCE MANAGEMENT 75
DRAWABLE FOLDERS
For Android, a drawable is simply something that can be drawn to the screen. Android
abstracts away the differences between colors, shapes, and images and allows you to
deal with a superclass that can represent any number of them: Drawable.
You keep the definitions for these objects in the drawable folders.
You should consider using a drawable for two main types of objects:
䊏 Image resources (mostly PNG files)
䊏 XML files describing things you want drawn: shapes, gradients, or colors
The drawable set of folders works similarly to the layout folders. You can specify
any mix of screen densities, layouts, or resolutions, provided you specify them in
the right order.
Just because you can have a whole different set of images for each screen
size or density doesn’t mean you actually should. Keep a close eye on the
size of your final compiled APK file. If the file grows over a certain size, users
will complain. To combat this bloat, use 9-patches, which are easily scaled
images, and use hand-rendered shapes over large PNG graphics where pos-
sible. While the new marketplace does allow for multiple APKs, it’s a level of
complexity you should avoid.
Layouts, from the simple to the complex, describe how to arrange a complex series
of views. This section covers the basics of Android’s arsenal of layout classes start-
ing with the ViewGroup, moving through LinearLayouts, and ending with the king
of the Android screen arrangers, the RelativeLayout.
THE VIEWGROUP
Each layout in Android extends what’s known as the ViewGroup. This is the class
of views that by definition can contain other views as children.
To demonstrate how each of the major layouts function, I’ll be laying out an
example picture-viewer screen. While Android provides its own snazzy photo
viewer, complete with a thumbnail carousel, mine will be simple and an excellent
vehicle for showing you how the various layouts work.
Figure 3.1 shows the screen that we’ll be rendering using the AbsoluteLayout,
the LinearLayout, and the RelativeLayout.
LAYOUT MANAGEMENT 77
The example picture viewer has two sections: the button bar with title and
the image itself. The button bar contains Next and Prev buttons and a text view
displaying the name of the image.
Before getting too deep into the layout, there are a few terms to watch for in
the XML:
dip or dp. This is how Android helps you scale your screen layout to
devices with different pixel densities. For example, on the Nexus S screen,
1dp = 1.5 pixels. It can be annoying to constantly convert the locations
onscreen to dips, but this small investment in time will pay huge divi-
dends when you’re running on a multitude of Android screens. Example:
android:padding=”20dp”.
px. Use this suffix to define an absolute pixel value for the specified dimen-
sion. In most cases, you should avoid declaring the absolute pixel value and
use dp. Example: android:paddingLeft=”15px”.
With a few simple definitions out of the way, I can start in on the various layout
classes. I’ll start with one that you’ll find appealing but that you should never use
in practice.
THE ABSOLUTELAYOUT
The most important thing you should know about AbsoluteLayouts is that you
should never use them. They are the quintessential beginner’s trap. They appear
to be a good idea at first (as they quickly give you exact pixel design), but they can
be frustrating, require excess time laying out new screens, and cause frequent
face-desk interaction. Consult your local Android expert if you experience any of
LAYOUT MANAGEMENT 79
android:layout_height=”wrap_content”
android:layout_x=”110dp”
android:layout_y=”0dp”
android:gravity=”center_horizontal”
android:text=”The Golden Gate”
/>
<Button
android:id=”@+id/next”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:text=”@string/next_string”
android:layout_x=”271dp”
android:layout_y=”0dp”
android:lines=”1”
android:padding=”12dp”
/>
<ImageView
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:layout_x=”0dp”
android:layout_y=”20dp”
android:gravity=”center”
android:id=”@+id/main_image”
android:src=”@drawable/bridge”
/>
</AbsoluteLayout>
Nothing in this layout code should look shocking. Each button, text view, and
image view has x and y coordinates. This simple code arranges the views to look
very similar (Figure 3.2) to the original.
All right, Figure 3.2 looks similar to Figure 3.1, but hang on, this is only one of
the 20-something ways that you can consume a single layout. Let’s look at what
happens to this layout when switching to landscape (Figure 3.3).
Yikes, that looks . . . bad! This is no way for a screen to look on any layout. Sure,
you could do an alternative layout for every portrait and landscape screen out there,
but that could add weeks to your schedule, and, worse, every time you need a new
screen aspect you’ll have to start from scratch.
While the AbsoluteLayout can give you a pixel-perfect design, it can achieve that
look for only a single screen configuration. Given that Android has many dozens of
screen sizes and types spread across a huge number of physical devices, layouts like
these will make your life miserable once you move beyond your initial test device.
The only way to make life harder for yourself is to use an AbsoluteLayout with
its children views defined in exact pixels (px) x and y values. Not only will a screen
laid out in this way break when you switch to landscape, but it’ll break when you
LAYOUT MANAGEMENT 81
switch from, say, the Nexus S to the HTC Hero, because they each have a different
number of pixels on the screen.
I’ve included the AbsoluteLayout in this chapter because if I didn’t, you might
find it on your own and wonder at what a gem you’d found. This is a cautionary
tale. The other layouts can be frustrating and time consuming up front, but trust
me, they’ll pay off in the end.
Bottom line: Don’t use AbsoluteLayouts except for extremely simple cases. I
could see it used to lay out a small, sophisticated button that could then be dropped
into one of the more dynamic layout classes. . .but please, for your own sanity, don’t
use this layout object unless you absolutely cannot avoid it.
THE LINEARLAYOUT
A LinearLayout is the exact opposite of the AbsoluteLayout. Within it, you’ll
define a series of views, and the system will size and place them dynamically on
the screen in the order you’ve specified. This layout is, and I cannot emphasize
this enough, not very good for putting views exactly where you want them. I’m
saving the layout class that is best at this for last.
Here’s how the original picture viewer looks when designed to work with a
LinearLayout:
<LinearLayout
xmlns:android=”http://schemas.android.com/apk/res/android”
android:layout_width=”match_parent”
android:layout_height=”match_parent”
android:orientation=”vertical”
>
<LinearLayout
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
android:orientation=”horizontal”
android:background=”#333333”
android:id=”@+id/button_bar”
>
LAYOUT MANAGEMENT 83
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:gravity=”center_horizontal”
android:id=”@+id/main_image”
android:src=”@drawable/bridge”
/>
</LinearLayout>
Notice that there are two LinearLayouts at work in this example. The top-level
layout contains the second LinearLayout object and the image to be displayed.
The second LinearLayout contains the two buttons and the text of the title. Two
layouts are required, in this case, because any one LinearLayout may have only
one orientation.
LinearLayouts can only lay out their children in one of two orientations: hori-
zontal or vertical. This can, if you’re not careful, lead to screens with heaping
piles of nested layout objects. Go too deep into a set of nested layouts and
your screen will render at the speed of a herd of turtles. Android engineers
suggest keeping the depth of your layout to less than ten. This is to say, don’t
create a view hierarchy with more than ten nested layouts of any kind. In
practice, do everything you can to use as few layouts as possible, because
you’ll start to see slowdowns on older, slower phones when your layout
depths are greater than seven or eight. If you find you’ve gotten in over
your head with a stack of LinearLayouts, consider refactoring with a single
RelativeLayout instead.
Figure 3.4 shows what this example XML produces in portrait mode.
This is where Android’s dynamic layouts really start to shine; take a look at
what the exact same layout code looks like when the user shifts into landscape
mode (Figure 3.5).
Not perfect, but a vast improvement over the AbsoluteLayout’s version of the
landscape screen.
USING LINEARLAYOUTS
When using the LinearLayout, the order in which you define your views is the order
in which they’re placed on the screen. First, take a look at that second LinearLayout:
<LinearLayout
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
android:orientation=”horizontal”
android:background=”#333333”
android:id=”@+id/button_bar”
>
LAYOUT MANAGEMENT 85
ONE VIEW MUST BE IN CHARGE OF SIZE
One view, or layout, must define a height and width value. You cannot tell
Android that you’d like a layout to wrap its content while, at the same time,
telling a view that it should match its parent. This will cause a compile error
because Android has no idea how big to make either the parent view or the
child layout. Something must define an actual height, even if it’s an image
or a line of text.
android:id=”@+id/prev”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:paddingLeft=”5dip”
android:paddingRight=”5dip”
android:text=”@string/prev_string”
android:lines=”1”
android:layout_weight=”1”
/>
The layout_weight value, which can be any decimal number from 0 to 1, tells
the system how much space to give to the child view when the layout has either
too much or too little space.
Take a look at the landscape version of the button bar again (Figure 3.6).
I’ve said it once and I’ll now say it a second time: LinearLayouts are not great
for laying out pixel-perfect screens. As views get proportionally larger and
smaller, it becomes difficult to get them to rest exactly where you want them
to. Use the LinearLayout when you have a known quantity of objects that
must get on the screen and you’re not overly picky about exactly where they
end up. In small, space-constrained cases, as with small button bars or single
list menu entries, AbsoluteLayouts can prove to be much easier to use to get
exactly the screen layout you’re looking for. This might be the only case I can
think of where an AbsoluteLayout might make sense.
LAYOUT MANAGEMENT 87
FIGURE 3.7 Oops, too small!
This may be very close to the way the screen looks in portrait mode, but not
close enough. As the top bar grows, the LinearLayout hands out extra pixel space
in proportion to the measured size of the child view. In this case, because all three
elements (two buttons and the text title) are weighted the same (1), the layout
divides the extra space evenly among them. You can re-weight the buttons such
that they grow in different ratios to the text title. Since you want the buttons to
grow at a slower rate than the text, you could try setting their weights to 0. Here
are what the new values look like (with everything else omitted):
<LinearLayout
>
<Button
android:layout_weight=”0”
/>
<TextView
android:layout_weight=”1”
/>
<Button
android:layout_weight=”0”
/>
</LinearLayout>
Figure 3.7 shows what this change does to the button bar.
Well, technically that’s correct, but it looks awful. You have two options. You
can declare exactly how much extra space you’d like each of the two buttons to
have through the android:padding declaration, or you can give them a little bit
more weight. You can fiddle with the first option on your own, but let’s take a look
at the padding option together.
<Button
android:layout_weight=”.25”
/>
<TextView
android:layout_weight=”1”
/>
<Button
android:layout_weight=”.25”
/>
</LinearLayout>
LAYOUT MANAGEMENT 89
FIGURE 3.10 Designing the
same screen, except with
the RelativeLayout.
THE RELATIVELAYOUT
The RelativeLayout is the mack daddy of the Android screen layout system. It
allows you to position all your child views in relation to either the parent (the layout
itself) or any other child view within the layout. Let’s take a look at one in action.
Here’s the now familiar image and button arrangement in the photo viewer, but
with a RelativeLayout (Figure 3.10).
There are a few slight measurement differences between this image and the one
produced with the LinearLayout. This one is also missing the gray background
behind the buttons, which I’ll show you how to add shortly.
Take a look at the XML layout that produced the image in Figure 3.10.
LAYOUT MANAGEMENT 91
android:text=”The Golden Gate Bridge”
/>
<ImageView
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:layout_alignParentLeft=”true”
android:layout_alignParentRight=”true”
android:layout_below=”@id/text_view”
android:gravity=”center”
android:id=”@+id/main_image”
android:src=”@drawable/bridge”
/>
</RelativeLayout>
In this layout code, you see the same view components that made up the Linear
Layout except, with the relative version, there’s no need for a second, nested layout.
The mechanics to each member of a RelativeLayout can be more complex than its
linear cousin, so I’ll endeavor to break down all four pieces of this screen one at a time.
The <RelativeLayout> declaration contains only enough information to tell
the layout to fill the entire screen. All information on how to lay out the screen is
found in the child elements. Here’s the first one:
<Button
android:id=”@+id/prev”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:padding=”15dip”
android:text=”@string/prev_string”
android:layout_alignParentLeft=”true”
android:layout_alignParentTop=”true”
/>
<Button
android:id=”@+id/next”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:padding=”15dip”
android:text=”@string/next_string”
android:layout_alignParentRight=”true”
android:layout_alignParentTop=”true”
/>
LAYOUT MANAGEMENT 93
The Next button is nearly identical to the Prev button except for the ID (required
to set up a click listener in the activity), the text displaying on the button (“next”),
and the android:layout_alignParentRight=”true” attribute (to lock it to the
right side of the layout object—and thus the right side of the screen—instead of
the left). Here’s the code for the title:
<TextView
android:id=”@+id/text_view”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:layout_toRightOf=”@id/prev”
android:layout_toLeftOf=”@id/next”
android:layout_alignParentTop=”true”
android:layout_centerHorizontal=”true”
android:layout_alignBottom=”@id/prev”
android:text=”The Golden Gate Bridge”
android:gravity=”center”
/>
In this text view, things start to get a little more interesting. Again, the ID, height,
and width are things you’ve seen before, but you need to change the title text as
the images change. As the image changes, you’ll need an ID so the activity can
change the name of the picture displayed above it.
android:layout_toRightOf=”@id/prev” tells the layout to align the left edge
of the text view with the right edge of the Prev button. android:layout_toLeftOf=
”@id/next” tells the right edge of the text view to align with the left-most edge of
the Next button. The android:gravity=”center” attribute tells the text to center
itself within any extra available space. This will center it vertically (so it doesn’t
stick to the top of the screen) and horizontally (so it doesn’t stick against the left-
most button).
LAYOUT MANAGEMENT 95
I want the gray box to be drawn behind the button bar, so I placed it as the first
view in the layout. Android draws the view stack in the order they’re declared. So,
were I to incorrectly place the listed XML below the button and text declarations,
you’d see only the gray bar covering over both the text and the buttons.
With that, you’ve successfully added a gray background and brought the Relative
Layout version of this view into parity with the earlier LinearLayout demonstra-
tion. The RelativeLayout can handle more complex displays without requiring
other nested layouts. It also can, if you’re smart about it, handle changes in screen
size, as shown by having the image’s name float between the buttons no matter
how far apart they get.
WRAPPING UP 97
4
ACQUIRING DATA
While the prime directivee off this chapter is to
to
teach you how to acquire data
ata from a remote source, this
is really just a sneaky way for me to teach you about Android
and the main thread. For the sake of simplicity, all the examples
in this chapter will deal with downloading and rendering image
data. In the next chapter, on adapters and lists, I’ll introduce you
to parsing complex data and displaying it to users. Image data, as
a general rule, is larger and more cumbersome, so you’ll run into
more interesting and demonstrative timing issues in dealing with it.
99
THE MAIN THREAD
The Android operation system has exactly one blessed thread authorized to change
anything that will be seen by the user. This alleviates what could be a concurrency
nightmare, such as view locations and data changing in one thread while a differ-
ent one is trying to lay them out onscreen. If only one thread is allowed to touch
the user interface, Android can guarantee that nothing vital is changed while it’s
measuring views and rendering them to the screen. This has, unfortunately, seri-
ous repercussions for how you’ll need to acquire and process data. Let me start
with a simple example.
This is exactly what I did when initially faced with the same problem. While
this code will fetch and display the required bitmap, there is a very sinister issue
lurking in the code—namely, the code itself is running on the main thread. Why
is this a problem? Consider that there can be only one main thread and that the
main thread is the only one that can interact with the screen in any capacity. This
means that while the example code is waiting for the network to come back with
image data, nothing whatsoever can be rendered to the screen. This image-fetching
code will block any action from taking place anywhere on the device. If you hold
the main thread hostage, buttons will not be processed, phone calls cannot be
answered, and nothing can be drawn to the screen until you release it.
WATCHDOGS
Given that a simple programmer error (like the one in the example code) could
effectively cripple any Android device, Google has gone to great lengths to make
sure no single application can control the main thread for any length of time. Hog-
ging too much of the main thread’s time will result in this disastrous dialog screen
(Figure 4.1) showing up over your application.
Anytime you see an ANR crash, Android will write a file containing a full
stack trace. You can access this file with the following ADB command line:
adb pull /data/anr/traces.txt. This should help you find the offending line.
The traces.txt file shows the stack trace of every thread in your program. The
first thread in the list is usually the one to look at carefully. Sometimes, the
long-running blocking operation will have completed before the system starts
writing traces.txt, which can make for a bewildering stack trace. Your long-
running operation probably finished just after Android started to get huffy
about the main thread being delayed. In the example code that displays the
image, however, it will probably show that httpCon.getResponseCode() was
the culprit. You’ll know this because it will be listed as the topmost stack trace
under your application’s thread list.
WHAT NOT TO DO
What kind of things should you avoid on the main thread?
䊏 Anything involving the network
䊏 Any task requiring a read or write from or to the file system
䊏 Heavy processing of any kind (such as image or movie modification)
䊏 Any task blocking a thread while you wait for something to complete
Excluding this list, there isn’t much left, so, as a general rule, if it doesn’t involve
setup or modification of the user interface, don’t do it on the main thread.
You can see why holding the main thread hostage while grabbing a silly picture
of the Golden Gate Bridge is a bad idea. But how, you might be wondering, do I
get off the main thread? An inventive hacker might simply move all the offending
code into a separate thread. This imaginary hacker might produce code looking
something like this:
At its core, the AsyncTask is an abstract class that you extend and that provides
the basic framework for a time-consuming asynchronous task.
The best way to describe the AsyncTask is to call it a working thread sand-
wich. That is to say, it has three major methods for which you must provide
implementation.
1. onPreExecute takes place on the main thread and is the first slice of bread.
It sets up the task, prepares a loading dialog, and warns the user that some-
thing is about to happen.
3. onPostExecute will be called once your work is finished (again, on the main
thread), and the results produced by the background method will be passed
to it. This is the other slice of bread.
That’s the gist of the asynchronous task. There are more-complicated factors
that I’ll touch on in just a minute, but this is one of the fundamental building blocks
of the Android platform (given that all hard work must be taken off the main thread).
Take a look at one in action, then we’ll go over the specifics of it:
That, dear readers, is an asynchronous task that will download an image at the
end of any URL and display it for your pleasure (provided you have an image view
onscreen with the ID remote_image). Here is how you’d kick off such a task from
the onCreate method of your activity.
Once you call execute on the ImageDownloader, it will download the image,
process it into a bitmap, and display it to the screen. That is, assuming your image_
layout.xml file contains an ImageView with the ID remote_image.
In this example, these are the classes that will be passed to your three major
methods.
ONPREEXECUTE
protected void onPreExecute(){
}
DOINBACKGROUND
protected Bitmap doInBackground(String... params) {
}
This is your chance to make as many network connections, file system accesses,
or other lengthy operations as you like without holding up the phone. The class of
object passed to this method will be determined by the first generic object in your
AsyncTask’s class declaration. Although I’m using only one parameter in the code
sample, you can actually pass any number of parameters (as long as they derive
from the saved class) and you’ll have them at your fingertips when doInBackground
is called. Once your long-running task has been completed, you’ll need to return
the result at the end of your function. This final value will be passed into another
method called back on the main UI thread.
Remember that mobile applications are not like their web or desktop coun-
terparts. Your users will typically be using their phones when they’re away
from a conventional computer. This means, usually, that they’re already wait-
ing for something: a bus, that cup of expensive coffee, their friend to come
back from the bathroom, or a boring meeting to end. It’s very important,
therefore, to keep them from having to wait on anything within your appli-
cation. Waiting for your mobile application to connect while you’re already
waiting for something else can be a frustrating experience. Do what you can
to limit users’ exposure to full-screen loading dialogs. They’re unavoidable
sometimes, but minimize them whenever possible.
As always, be careful when doing UI updates, because if the activity isn’t cur-
rently onscreen or has been destroyed, you could run into some trouble.
I take the bitmap downloaded from the website, retrieve the image view into
which it’s going to be loaded, and set it as that view’s bitmap to be rendered. There’s
an error case I haven’t correctly handled here. Take a second to look back at the
original code and see if you can spot it.
You might be curious as to exactly what you should use in these cases. I’m glad
you are, because that’s exactly what I’d like to show you next.
The IntentService is an excellent way to move large amounts of data around without
relying on any specific activity or even application. The AsyncTask will always take
over the main thread at least twice (with its pre- and post-execute methods), and it
must be owned by an activity that is able to draw to the screen. The IntentService
has no such restriction. To demonstrate, I’ll show you how to download the same
image, this time from the IntentService rather than the AsyncTask.
DECLARING A SERVICE
Services are, essentially, classes that run in the background with no access to the
screen. In order for the system to find your service when required, you’ll need to
declare it in your manifest, like so:
At a minimum, you’ll need to have this simple declaration. It will then allow
you to (as I showed you earlier with activities) explicitly launch your service. Here’s
the code to do exactly that:
Notice that the constructor you must declare has no string as a parameter. The
parent’s constructor that you must call, however, must be passed a string. Eclipse
will make it seem that you must declare a constructor with a string when, in reality,
you must declare it without one. This simple mistake can cause you several hours
of intense face-to-desk debugging.
Once your service exists, and before anything else runs, the system will call
your onCreate method. onCreate is an excellent time to run any housekeeping
chores you’ll need for the rest of the service’s tasks (more on this when I show
you the image downloader).
At last, the service can get down to doing some heavy lifting. Once it has been
constructed and has had its onCreate method called, it will then receive a call to
handleIntent for each time any other activity has called startService.
FETCHING IMAGES
The main difference between fetching images and fetching smaller, manageable
data is that larger data sets (such as images or larger data retrievals) should not be
bundled into a final broadcast intent (another major difference to the AsyncTask).
Also, keep in mind that the service has no direct access to any activity, so it cannot
THE SETUP
Before you can use the external storage to cache the data, you’ll need to create a
cache folder for your application. A good place to check is when the IntentService’s
onCreate method is called:
Using Android’s environment, you can determine the correct prefix for the
external file system. Once you know the path to the eventual cache folder, you can
then make sure the directory is in place. Yes, I know I told you to avoid file-system
contact while on the main thread (and onCreate is called on the main thread),
but checking and creating a directory is a small enough task that it should be all
right. I’ll leave this as an open question for you as you read through the rest of this
chapter: Where might be a better place to put this code?
When you’re saving files to the SD card, you must also be aware that nearly
all pre-2.3 Android devices can have their SD cards removed (or mounted as a
USB drive on the user’s laptop). This means you’ll need to gracefully handle
the case where the SD card is missing. You’ll also need to be able to forgo the
file-system cache on the fly if you want your application to work correctly
when the external drive is missing. There are a lot of details to be conscious
of while implementing a persistent storage cache, but the benefits (offline
access, faster start-up times, fewer app-halting loading dialogs) make it
more than worth your effort.
THE FETCH
Now that you’ve got a place to save images as you download them, it’s time to
implement the image fetcher. Here’s the handleIntent method:
This code registered a receiver (so you can take action once the download is
finished), started the service, and, finally, showed a loading dialog to the user.
Now take a look at what the imageReceiver class looks like:
Loader is a new class that comes both in Honeycomb and in the Android
Compatibility library. Sadly, there is not enough space in this chapter to cover
it in detail, but I will say that it’s an excellent tool to explore if you must
do heavy lifting off the main thread repeatedly. It, like AsyncTask, is usually
bound to an activity, but it is much better suited to handle situations where
a single task must be performed many times. It’s great for loading cursors
(with the CursorLoader subclass) and for other tasks, like downloading indi-
vidual list items for a ListView. Check the documentation for how best to use
this new and powerful class.
That about covers us on how to load data. Remember, loading from the SD card,
network transactions, and longer processing tasks MUST be performed off the
main thread, or your application, and users, will suffer. You can, as I’ve shown
you in this chapter, use a simple thread, an AsyncTask, or an IntentService to
retrieve and process your data. But remember, too, that any action modifying any
view or object onscreen must be carried out on the main thread (or Android will
throw angry exceptions at you).
Further, keep in mind that these three methods are only a few of many possible
background data fetching patterns. Loaders, Workers, and ThreadPools are all other
alternatives that might suit your application better than the examples I’ve given.
Follow the simple rules I’ve outlined here, and your app will be fast, it will
be responsive to your users, and it will avoid the dreaded App Not Responding
notification of doom. Correct use and avoidance of the main thread is critical to
producing a successful application.
If you’re more interested in building lists out of complex data from remote
sources, the next chapter should give you exactly what you’re looking for. I’ll be
showing you how to render a list of Twitter messages to a menu onscreen.
I’ll leave you with a final challenge: Enable Android’s strict mode and move
the little file accesses I’ve left in this chapter’s sample code off the main thread. It
should be a good way to familiarize yourself with the process before you undertake
it on your own.
125
TWO PIECES TO EACH LIST
To display lists of ordered data with Android, there are two major components
you’ll need to deal with.
LISTVIEW
First, you’ll need a ListView in which to display your content. This is the view whose
job it is to display the information. It can be added to any screen layout, or you can
use Android’s ListActivity or ListFragment to handle some of the organization
for you. If your screen is primarily designed to show a collection of data to the user
in list form, I highly suggest you use ListActivity and its cousin ListFragment.
ADAPTER
The second major class you’ll need to deal with is the Adapter. This is the object
that will feed the individual views, a little bit at a time, to the ListView. It’s also
responsible for filling and configuring the individual rows to be populated in the
ListView. There are as many Adapter subclasses as drops of water in the ocean (all
right, perhaps slightly fewer), and they cover the range of data types—from static
string lists (ArrayAdapters) to the more dynamic lists (CursorAdapters). You can
extend your own adapter (which I’ll show you in the second half of this chapter).
For now, let me show you how to create a simple main menu with a ListView.
As always, you can either follow along with the sample code I’ve posted at
Peachpit.com/androiddevelopanddesign or open your IDE and do the tasks I’ve
outlined.
Main menus can take any number of forms. From games to music apps, they provide
a top-level navigation into the app as a whole.
They are also, as a happy side effect, a great way to introduce you to how lists
work. I’ll be creating an array of strings for the resource manager, feeding it to an
array adapter, and plugging that array adapter into the list view contained by a list
activity. Got all that? There are a lot of moving parts to collect when dealing with
lists, so I’ll take it slowly and step by step.
Instead of defining each constant inside a string tag, this time you’ll declare a
string array with a name, and then each element within it can be defined inside an
item tag. Now that you have data, it’s time to create an activity in which to house it.
package com.haseman.lists;
import android.app.ListActivity;
import android.os.Bundle;
public class MainMenuActivity extends ListActivity{
public void onCreate(Bundle bundle){
super.onCreate(bundle);
setContentView(R.layout.list_activity);
}
}
This code will not, however, compile at the moment, because I haven’t yet defined
what R.layout.list_activity looks like. Guess what you’re going to do next?
This XML layout code should look familiar to you, given what you’ve read in
previous chapters. It’s simply splitting the screen space between the title main
menu and the list of sub-screens. You can also see the special Android list ID that
is needed to tell the ListActivity which view it should interact with.
Because the ListView has the special @android:id/list system ID, the List
Activity knows where to find the ListView. As a result, you’ll only have to create
the adapter and hand it over to the ListActivity. The ListActivity will make sure
that it’s correctly plugged into the ListView and that everything is drawn correctly.
To create the ArrayAdapter, I specify the array of strings I defined in the section
“Creating the Menu Data” as well as the list_element layout I created in “Making a
Menu List Item.” Assuming that all your Tab A’s are correctly fitted into your Slot
B’s, the resulting screen will look something like Figure 5.1.
Do a little dance—you’ve now got a functional (albeit very simple) list! Have a
smoke, cup of coffee, sip of wine, or dog treat. Whatever you do to reward yourself
for a job well done, do it now. I’ll be here when you get back.
@Override
public void onListItemClick(ListView lv,
View clickedView,
int position, long id)
{
super.onListItemClick(lv, clickedView, position, id);
TextView tv = (TextView)clickedView;
Toast.makeText(getApplicationContext(),
“List Item “+tv.getText()+” was clicked!”,
Toast.LENGTH_SHORT).show();
}
The ListActivity will call this method (if you’ve defined it) every time an
element in the list view is clicked (or tapped with a finger). For more-complicated
lists, you may want to use the ID (especially if you are using SQLite as a backing
store). For this simple demo, I’ve just popped up a little dialog showing the text of
the item that was pressed. If you’re implementing your own basic main menu, I
suggest you use the position of the clicked item to start an activity, service, or other
action. You can see an example of this if you look at the associated source code.
That’s the most basic list view I could possibly show you. Now, I’ll take you in
the opposite direction and show you what a custom list backed by a remote data
source looks like.
While building a main menu is great and all, there are much more complicated uses
to which you can put the Adapter and ListView combination. In fact, I’m going to
show you an example that gets complicated in two ways. First, the data source is
going to be from a remote URL (a Twitter feed). Second, I’m going to add a second
text view to the list (you could, if you want to, add any number of items to it).
NOTE: If the Twitter URL in the earlier code listing isn’t working,
I’ve stashed a backup copy of the data at http://wanderingoak.net/
twitter_backup.json. If Twitter changes their API, you can always run
the sample code against that URL.
HANDLING EXCEPTIONS
It’s always a good idea to print out the cause of any particular exception
when it’s caught. This is, at its core, just Java best practices. If an exception
comes through and you’re not printing it to Android’s LogCat tool, things
can get very confusing, very fast. When beginners have trouble debugging
a problem, it’s often because they’re catching and releasing exceptions that
contain important information.
1. Create a JSON array from the string that was fed back to you in getTwitter
Feed. This will parse the data, which you then return at the end of the
doInBackground method.
2. Once you’re back on the main thread inside the onPostExecute method, it’s
time to pass the JSON array to the custom adapter (which I promise to show
you in just a second). Updating the data will trigger a redraw of the list view.
3. Hide the initial “Loading . . . ” TextView that I defined in the XML layout file,
and show the list instead.
NOTE: Any changes to the Adapter’s data must take place on the main
thread. Modifying the Adapter data counts as changing the UI, as far as
Android is concerned. As always, all changes to the user interface must
be carried out on the main thread. Keep this is mind as you create
your own adapter, especially if you’re fetching data from the network
off the main thread.
As you can see by the get prefix on all the required methods, all that Android
Adapters do is provide row content information to the ListView. The ListView,
it would seem, is one very needy girlfriend (or boyfriend . . . I’m not sure how to
assign gender to Android UI interfaces).
Let me show you the example before I talk about any more theory. Twitter’s
API returns its information in the form of JSON-encoded objects. It doesn’t, at this
point, make sense to translate it to some other data store, so I’ll design my custom
adapter to use a JSONArray object as its data backer. This class is declared as an
inner class definition in ListActivity.
This code, for the most part, wraps accessors to the JSON object. It handles
getting an item from a position (which in this example is the index into the JSON
array). If no data has been set, then the Adapter simply reports that there’s nothing
to see. The only method in the example that doesn’t override a required function
is the code that changes the data set. It also calls notifyDataSetChanged and, as
a result of this method, must be called on the main thread. My class extends from
BaseAdapter because it contains all the baseline methods that I need to build my
custom adapter.
@Override
public View getView(int position, View convertView,
p ViewGroup parent) {
JSONObject node = (JSONObject)getItem(position);
ViewGroup listView = null;
//Reduce, Reuse, Recycle!
if(convertView == null)
listView =
(ViewGroup)getLayoutInflater().inflate
(R.layout.twitter_list_item, null);
else
listView = (ViewGroup)convertView;
try{
boolean retweeted = node.getInt(“retweet_count”) > 0;
TextView tv =
(TextView)listView.findViewById(R.id.text_one);
tv.setText(node.getString(“text”));
if(retweeted)
tv.setTextColor(0xFFFF0000);
else
tv.setTextColor(0xFFFFFFFF);
tv = (TextView)listView.findViewById(R.id.text_two);
tv.setText(node.getString(“created_at”));
There are a couple of key points to consider in the getView code listing.
First, you need to figure out if the view can be recycled. If it can, you’ll reset all
the visible values for it; otherwise, you’ll inflate a new row—by using the Layout
Inflater—and configure it (more on how and why this works soon).
Second, you’ll detect, from the JSONObject, if the message has been retweeted
by checking the retweet count. If it has, you’ll set the text color for both text views.
Last, you’ll pull both the text and created_at strings from the JSONObject and
set them as the two text views. You might have noticed that I haven’t shown you
what twitter_list_item.xml looks like. That is the view layout I’m creating (by
calling the inflate method and passing in the layout).
android:id=”@+id/text_one”
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
android:padding=”6dp”/>
<TextView
android:id=”@+id/text_two”
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
android:padding=”6dp”/>
</LinearLayout>
With this layout, you now have all the moving pieces you need to download,
parse, and display a Twitter feed. Figure 5.2, at last, is what Peachpit’s Twitter feed
looks like in ListView form.
To understand how the ListView interacts with the Adapter, there are a few con-
straints you must understand. First, lists could scroll on to infinity, at least from
the point of view of the device. Yet, as you might have guessed, the phone has a
limited amount of memory. This means that not every single list item can have its
own entry in the list, because the device would quickly run out of space. Further,
if the ListView had to lay out every single row right up front, it could be bogged
down for an unacceptable amount of time.
What Android does to solve these problems is to recycle list element rows. The
process looks a little bit like this:
1. Android goes through the entire list, asking each row how large it would like
to be (this is so it knows how large to draw the scroll indicator).
2. Once it knows roughly how big the entire ListView will be, it then requests
views for the first screen, plus a buffer (so it won’t have to stop and get more
when the user starts scrolling). Your adapter will have to create, configure,
and return those views as the ListView calls getView over and over again.
3. As the user scrolls down and rows fall off the top of the list, Android will
return them to you when it calls getView. Effectively, it’s asking you to reuse
a previous view by passing in the convertView object to you.
While recycling list element rows is great for conserving memory and speeding
up long lists, it has some interesting side effects.
䊏 All your list views, in order to take advantage of the built-in recycling, must
always inflate from the same row layout. Android won’t know what type
of list item you’ll want to create; so if you had, for example, three different
row layouts, the system would not know which one to pass back to you, and
you’d have to handle your own pools of unused views.
䊏 Any asynchronous task, such as loading an icon from disk or loading a user’s
profile icon, must check that the ListView hasn’t recycled the view while
it’s been downloading or loading the image data. If the row is still showing
the same data when the task finishes, it’s safe to update the row; otherwise,
it needs to cache or chuck the data.
This chapter covered the basics of both simple and custom ListViews and Adapters.
I showed you how to create a simple main menu, and I walked you through a simple
example of building a custom Adapter to handle a more complex ListView. You now
have a grasp of the basics.
Lists are still one of the cornerstones of mobile development. I advise you,
however, to make as few boring, graphically flat lists as you possibly can. While
these examples are great for showing you how to build lists of your own, they are
by no means shining examples of solid interface design. You can, and very much
should, make lists when needed, but dress them up as much as you can without
affecting performance.
If you’re hungering for more, I highly suggest reading through Android’s imple-
mentation of ListActivity.java. Because Android is open source, you can get all
the code that makes up its SDK for free! Head over to http://source.android.com
for more information.
Lastly, I wrote more code for this chapter than I had space to explain here. I recom-
mend checking out the sample code associated with this chapter (at Peachpit.com/
androiddevelopanddesign) to learn more about launching a screen as the result of a
menu click and about how to build a similar main menu screen using a ListFragment.
WRAPPING UP 145
6
THE WAY
OF THE SERVICE
Services are one of the most
mosst important, and
an
nd
often under-utilized, components
mponents
ponents of the Android plat
plat--
form. They are essential for
or accomplishing any task whose
data or relevance can span more than one activity. They are like
activities in that they have a lifecycle (albeit a much simpler one),
but they do not have the activity’s ability to draw to the screen.
In practice, services break down into two major use cases: the
listener and the task. Listeners are services that hang out in the
background, waiting for something to happen that prompts them
to take action. Task services are akin to the photo downloader that
we covered before, so in this chapter I’ll focus on listening services.
147
WHAT IS A SERVICE?
A Service is, at its most basic level, a class with a simple runtime lifecycle and
no access to the screen. You had some contact with the IntentService back in
Chapter 4 when I showed you how to retrieve an image with it, but I now have the
chance to help you really dig into this simple yet powerful component.
Keep in mind that while the service might be important to you, it is not more
important to Android than the smooth running of the overall device. This means
that at any point the system may shut down your service if it determines that it’s
been running too long, that it’s been consuming too many resources, or that it’s the
third Friday of the month and there’s a full moon. There is a way to tell the system
not to kill you off, and I’ll show what that looks like in just a second.
At this point in the lifecycle, your service is now happily running along. Music
can be played, data can be acquired (remember the main thread!), and record-
ings can be made and crunched for voice commands.
Just because you’re running in a service doesn’t mean you’re off the main
thread! The service’s onCreate method will be called on the main thread. If you
need to do any heavy lifting, consider using an AsyncTask or a Handler+Looper
pattern, or instead of doing a normal service, use an IntentService to process
heavy data off the main thread. Further, there are certain actions (like record-
ing audio) that you can initiate only from the main thread. It’s 10 a.m., do you
know where your threads are?
SHUT IT DOWN!
At some point, the party will end and it’ll be time to clean up. This can happen
because your service called stopSelf or because another component called
Context.stopService. Here’s the teardown portion of the lifecycle:
䊏 onDestroy is your chance to cancel any running tasks and put away any
resource you’ve taken on (for example, media or network tasks). This is also
your chance to unregister any BroadcastReceivers or ContentObservers
that you’ve set up to watch for new media.
If you were expecting a many-step shutdown process, I’m afraid you’re going
to be disappointed. Because services have no notion of being on top of the screen,
there is no need to pause, resume, or do any of the other complex interactions
that activities must support.
There are two main ways to communicate with a service: intent broadcasts and
binder interfaces. I’m going to show you examples of both and, along the way, let
you see two practical tasks for a service. There are, in fact, many more ways you
can communicate with your services. But in my experience, these are the two most
useful. As always, check the documentation if neither of these approaches feels
quite right for you.
INTENT-BASED COMMUNICATION
Imagine two workers in different rooms who can only communicate with each other
by email. These emails can contain attachments and other pieces of data. The two
workers must get through their day using only this one method of communication.
As you might imagine, this can be an efficient and functional way to get a multitude
of things done, as long as they don’t have to say too much to each other.
This is, in a sense, exactly what intent-based communication with services
would look like translated to real life. The service is started with an intent; when
it completes its task or something that it’s waiting for occurs, it sends a broadcast
intent alerting anyone listening that a particular task is finished. You saw one
example of this in Chapter 4 when I was downloading images using an intent
service. Let me show you one more.
The following example is one of the best examples of intent-based communication
that I can give you in this printed form. I’ll create all the pieces required for a new ser-
vice that runs, with a notification, in the foreground. I use Android’s ContentProvider
to listen for and acquire the location of new photographs as they are taken. This code
will alert you when any new picture is snapped, regardless of the application used
to do it. What you do with the photograph, I’ll leave to your boundless imagination.
5. Go to the foreground.
<service android:name=”PhotoListenerService”/>
Since onBind is a required method for the Service class, it has to be in my class
or it won’t compile. Now that you’ve got a service, let’s look at how to actually start it.
COMMUNICATION 151
STARTING THE SERVICE
Start the service from your activity. When I created the project, I got a default activ-
ity (I named mine, quite originally, ServiceExampleActivity) and a main.xml view.
1. Modify that view to contain Start and Stop buttons, like so:
<?xml version=”1.0” encoding=”utf-8”?>
<LinearLayout xmlns:android=”http://schemas.android.com/
p apk/res/android”
android:orientation=”vertical”
android:layout_width=”match_parent”
android:layout_height=”match_parent”
>
<Button
android:id=”@+id/start_service”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:text=”start service”
android:padding=”15dp”
android:gravity=”center”/>
<Button
android:id=”@+id/stop_service”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:text=”stop service”
android:padding=”15dp”
android:gravity=”center”/>
</LinearLayout>
The buttons are a simple way to put some clickable text on the screen.
COMMUNICATION 153
startService(serviceIntent);
}
else if(v.getId() == R.id.stop_service){
stopService(serviceIntent);
}
}
This, again, is pretty simple. The buttons, when clicked, will call this onClick
method. Depending on the view that actually got the click, I’ll either start or stop
the service.
@Override
public void onCreate(){
super.onCreate();
getContentResolver().
registerContentObserver(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
true,
observer);
}
I’m going to record when the service started (more on why that’s important in
just a second). Last, I return Service.START_STICKY, which tells the system that,
should the service be terminated for memory or performance reasons, I’d like to
have it started back up.
I need to write a function to handle creating a notification and moving the
service into the foreground. setForegroundState is my own method that handles
placing the service and removing the service from foreground mode.
COMMUNICATION 155
“com.haseman.serviceExample”,
R.layout.notification);
Intent clickIntent = new Intent(
getApplicationContext(),
ServiceExampleActivity.class);
n.contentIntent =
PendingIntent.getActivity(
getApplicationContext(), 0, clickIntent , 0);
startForeground(1, n);
}
else{
stopForeground(true);
}
}
Thanks to the wonders of line wrapping, this code is a little tricky to read. Essen-
tially, in order to go into foreground mode, you need at the very least a Notification
object, a RemoteView, and an intent that fires when the user clicks the pull-down
notification. Let me break it down a little more.
Start by creating a Notification object, which requires three things:
䊏 A small icon resource, to be constantly displayed along the top of the noti-
fication bar.
䊏 A string, to be briefly flashed along the top bar when the notification appears.
䊏 The time at which to display the notification (in this case, right now!). This
constructor returns a Notification object to which you can add the rest
of the required objects.
You’ll next see me create a RemoteView for the pull-down notification bar and
set it in the code listing as the notification’s contentIntent.
This is the layout that will be inflated and placed into the notification bar. You
can use only stock Android widgets for these (no custom views), and you’ll want
to keep it very simple. Mine consists of an icon and a text view. It’s simple enough
that you can probably imagine exactly what it looks like, but Figure 6.1 shows
what it looks like in action.
It’s worth noting that you are not actually inflating the view yourself when creat-
ing a RemoteView. You’re specifying a layout and then giving the system instructions
on what to do to it when it is eventually inflated (this is the difference between a
View and a RemoteView). You’ll set this as the notification’s contentView.
Next, you’ll see me create an intent that is to be fired when the user presses on
the notification row seen in Figure 6.1. This is a PendingIntent, which, again, is
really an intent and an instruction about what to do with it when the time comes.
In this case, I want to launch the activity with the Start and Stop buttons. You’ll set
the PendingIntent, as I did, to be the notification’s contentIntent.
With the fully built notification in hand, you can now call startForeground
and hand it the notification.
You’ve now met your half of the contract: You’ve told the user who you are and
why you’re running. The system now will allow you to run uninterrupted until such
time as the user disables the service by pressing the activity’s Stop button. It’s time
to start listening for when the content changes.
COMMUNICATION 157
OBSERVING WHEN CONTENT CHANGES
Now that the service is running and in the foreground, I can show you what an
empty ContentObserver looks like:
Each time any photo on the phone changes, onChange will be called. It is your
task, at that time, to determine what change actually took place. You should do this
by querying the ContentProvider for images that were created after the lastUpdate
Time. Here’s what that looks like in my sample code:
If this looks similar to a SQL query, it’s meant to. I’m specifying the URI that
I want information about (all images). The next parameter (to which I pass null)
would be my chance to list the specific columns I want to receive. By passing null,
I’ve asked for all of them. Next is the WHERE clause of the query, where I’m asking for
every photo created after the lastUpdateTime. Since I don’t have any more state-
ments, I’ll leave the next parameter null. Finally, the default sorting of results will
lastUpdateTime = System.currentTimeMillis();
if(cursor.moveToFirst()){
Intent i = new Intent(ACTION_PHOTO_TAKEN);
int dataIDX = cursor.getColumnIndex(MediaStore.Images.Media.DATA);
i.putExtra(“path”, cursor.getString(dataIDX));
sendBroadcast(i);
}
Now that I have the cursor, I can set the new lastUpdateTime. This will ensure
that this current picture will not show up in subsequent observer-fired queries.
Instead of starting an upload, I’m just sending an alert that a photo was taken and
where you can find that new file. I then move the cursor to its initial position and
make sure (by checking to see if it returns true) that there is actually a piece of
data to find. Next, I’ll get the column index for the data column. Last, I’ll retrieve
the path from the cursor and add it as an extra to my new intent. Last, I broadcast
the existence of the photo for anyone who might be listening for it.
It’s important to call cursor.close on every cursor you get back from a Content
Provider. Otherwise, the system will throw errors at you for leaking memory.
COMMUNICATION 159
BINDER SERVICE COMMUNICATION
On the very opposite side of the spectrum from intent-based communication is
a service controlled through a binder interface. This allows cross-process com-
munication with any other component that binds itself to your service. More than
one process or component can bind with any service singleton, but they will, of
course, all be accessing the same one.
Binding—and subsequently calling methods directly on the service—requires
a few steps in order to handle the exchange cleanly and efficiently.
As you can see, this is not to be embarked upon unless you really need tight
integration between your component and the service to which you’d like to bind.
In a later chapter, I’ll show you the ins and outs of media playback and recording.
To do this correctly, you’re going to need the kind of integration that only a service
with a bound interface can provide. So, by way of example, I’m going to build a
background service with an IBinder suitable to play music in the background. For
brevity, I’ll avoid discussing every single method, but I’ll cover a few basic functions
and show you how to establish the connection.
CREATING AN AIDL
The AIDL (Android Interface Definition Language) file is your chance to define the
interface through which your service can talk to the outside world. In my example,
I created the IMusicService.aidl file in the src/com/haseman/serviceExample/
directory. In it, you’ll declare an interface:
More methods will eventually be needed for a fully functional music service,
but for now this will do. When you next compile your project, Android will create
an IMusicService.java file (you can find it in Eclipse under the gen package) con-
taining all the Java code required to marshal the appropriate data across processes.
You might be wondering what that in prefix is doing in front of the parameter
declaration of setDataSource. This is how you tell the service that, in this case, the
service is the roach motel of method calls. The parameter goes in, but it doesn’t
come out (you won’t be modifying or changing it within your service). This allows
Android to marshal the variable across the processes once, but it means that it
doesn’t have to marshal it back out again, saving time and resources.
<service android:name=”MusicService”
android:process=”:music_service”/>
The colon (:) in the process tells the system to prefix your current package
name to it; otherwise, you can name the process anything you want. This way, the
system can keep your music service running in the background while, at the same
time, being able to shut down your larger application process, which is very handy
on resource-constrained phones.
COMMUNICATION 161
In the new service, I’ve declared all the same methods I had in the AIDL interface
file. It currently looks like this:
COMMUNICATION 163
You can see how it extends the IMusicService.Stub class and takes, in its
constructor, a pointer to the outer service that it wraps in a weak reference. You
need to do this because the system may keep a reference to the binder stub long
after the service’s onDestroy method has been called, and you’ll want the garbage
collector to be able clean up your service. For the curious, the weak reference allows
the wrapped class to be deallocated by the garbage collector if the weak reference
is the only remaining pointer to it. Quite handy in this case, assuming Android
actually honors weak references.
You can also see how I’m returning this as the IBinder object when onBind is
called. This MusicServiceStub will be the object that other components use to
communicate with the service.
Intent bindServiceIntent =
new Intent(getApplicationContext(), MusicService.class);
if(v.getId() == R.id.start_binder_service) {
bindService(bindServiceIntent, this, Service.START_NOT_STICKY);
} else if(v.getId() == R.id.stop_binder_service) {
unbindService(this);
}
You can see, in the highlighted code, that I’m binding with the service by giving it
䊏 An intent specifying which service I’d like to connect to.
䊏 A pointer to a ServiceConnection object (which I’ll make my activity
implement).
Now, my example code won’t compile until I actually add an implements Service
Connection to my activity’s class declaration and the required methods that it
entails. Here, in all their glory, are my activity’s new required methods.
IMusicService mService;
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mService = IMusicService.Stub.asInterface(service);
try {
mService.setDataSource(0);
} catch (RemoteException re) { Log.e(“MusicService”, “”,re}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
This code might look a little strange, but the asInterface call is converting
from the IBinder object to the IMusicService (which I can then make direct calls
on). However, each call to the remote service requires that you catch potential
RemoteExceptions that come up.
That’s really all there is to it. Once you’ve received the onServiceConnected
call and completed the conversion, you can stash that MusicService object for
whenever you need it.
But don’t forget to unbind from it when your onDestroy method gets called.
Creating an AIDL and binding to a service in this way is actually one of the
more complicated ways to communicate with a service. If you’re not going to be
building a long-running service in a separate process, this might not be the perfect
setup for you. Consider checking the SDK documentation for the locally bound
service or messenger patterns.
COMMUNICATION 165
WRAPPING UP
In this chapter, you learned how to use a simple foreground service to notify you
when a photograph is added to Android’s ContentProvider. You learned how to
start it, place it in the foreground with a notification, and then kill it off when the
user no longer wanted it to run. Next, I went from the simple to the complex and
showed you how to communicate directly with a service across process boundaries.
You did this by creating an AIDL interface, implementing a stub, and then using a
ServiceConnection and a bindService call to establish a connection with the service.
The first example was a simple service that does only one thing, while the
second example you stormed through was one of the more complex mechanisms
that Android can provide. If your arms are long enough (and you’re not a Tyran-
nosaurus rex), give yourself a resounding pat on the back.
169
UNCOVERING THE SECRETS
OF THE RES/ FOLDER
Earlier, I gave you a basic mapping of what goes where in the res/ folder. In this
section, I’ll show you its more advanced functions. As always, you can either
code this yourself or follow along from the sample code posted at Peachpit.com/
androiddevelopanddesign.
LAYOUT FOLDERS
The layout folders are the source of the first tool at your disposal, and it’s one of
the best. Android will, if configured correctly, pick layout files from a folder that
matches the hardware configuration closest to the one it’s running on. Using this
tool, you can define multiple screen layouts for any number of different hardware
configurations. Let’s start with something simple: landscape mode.
Let’s say you have a simple screen with two buttons. Let’s take a look at the
layout XML that produced the two buttons.
<RelativeLayout>
<!--Text view for question and relative layout params omitted-->
<Button
android:padding=”15dp”
android:gravity=”center”
android:id=”@+id/yes_button”
android:layout_marginLeft=”30dp”
android:layout_marginRight=”30dp”
android:layout_width=”fill_parent”
android:layout_height=”wrap_content”
android:layout_above=”@+id/no_button”
android:text=”@string/yes_button_text”
/>
<Button
android:padding=”15dp”
android:gravity=”center”
android:id=”@+id/no_button”
android:layout_marginLeft=”30dp”
android:layout_marginRight=”30dp”
android:layout_marginBottom=”60dp”
android:layout_width=”fill_parent”
android:layout_height=”wrap_content”
android:text=”@string/no_button_text”
android:layout_alignParentBottom=”true”
/>
What you see in this code listing shouldn’t be anything new, given your expe-
rience with the RelativeLayout in previous chapters. I’ve declared two buttons,
pinned the No button to the bottom, and aligned the Yes button above it. When
shown in portrait mode, these buttons look simple but pretty good (Figure 7.1).
While this will only win me a design award if the judges are partially blind, it’s
a fairly good-looking, simple, and functional screen. But take a look at what hap-
pens when I switch it to landscape mode (Figure 7.2).
2. Into this folder, place landscape-specific layout files that Android will use
automatically when the device is in landscape mode.
This is the file into which we’ll put the landscape-specific layout.
As you can see, I’ve created a horizontal layout with two buttons to be used
in landscape mode.
Don’t forget about the portrait layout. If we’re going to include a layout, it’s got
to exist for both the landscape configuration and the default configuration.
At this point, you’ve created two layouts: one for portrait and one for
landscape.
4. You can now modify your original XML with an include, like so:
<RelativeLayout>
<!--Text view and relative layout params omitted-->
<include layout=”@layout/button_layout”
android:id=”@+id/button_holder”
/>
</RelativeLayout>
With the <include> tag in place (instead of a single button definition), Android
will grab the button_layout.xml file that corresponds to the screen configura-
tion. If it can’t find one, it will default back to what’s in /res/layout.
Now, with this new code, the landscape mode looks much better (Figure 7.3).
Are there things that could be improved? Sure! Now, however, you know how
to specify that parts of your user interface should change as the screen’s hardware
configuration changes.
MERGING
You don’t have to wrap your excised views in a new ViewGroup (RelativeLayout
in the previous example) for them to be included (as I did here). If you don’t
want to add another layout to the mix but would like to bring in a series of
views from other XML files, simply wrap them in a <merge> tag.
<merge xmlns:android=”http://schemas.android.com/apk/res/android”>
<!-- views go here -->
</merge>
This will allow you to include views without adding another layout to your
view hierarchy.
The size modifier accounts for the physical size of the screen. Devices that
would use the small layout folder are typically very old, or very strange
pieces of hardware, at least until Android powered watches become popular.
Most modern phones fit the layout-normal category, while many tablets
are considered xlarge. Google keeps a great breakdown of all the various
screen configurations at http://developer.android.com/resources/dashboard/
screens.html.
䊏 layout-ldpi, layout-mdpi, layout-hdpi, layout-xhdpi
The dpi, or dots per inch, of the device is a measurement of screen density.
Screens with high densities (240 dpi) would pull from the layout folder
layout-hdpi.
䊏 layout-large-hdpi-land
You can also mix and match the suffixes. This suffix would be used for
phones that have large screens and high resolution and that are in land-
scape mode. Get creative, but remember that just because you can get very
specific about screen configurations, it doesn’t mean you should.
Instead of defining two small parts that change based on the screen con-
figuration, you have to make and test more than one entire screen layout.
Those two things aside, sometimes you really do need a completely separate
layout for a different hardware or screen configuration. Try to modify small parts
when you can, but don’t be afraid to crack your knuckles and make a totally new
screen layout when it’s necessary.
To make a new layout of the “chocolate” example, you can simply make a second
two_buttons.xml file in /res/layout-land and configure the screen in any way
you like. Then the call to setContentView in the onCreate method of your activity
will, like include, find the right resource for the right screen configuration.
Android can only find the layout XML by name. As long as the layout files
have the exact same filename, it will locate the version in your landscape
(or any other) folder.
䊏 Make sure that the IDs for your individual views are consistent.
Your activity shouldn’t try to change the position of things onscreen. While
in portrait mode, the button might be at point (330, 120); it will be some-
where totally different in landscape mode. In this situation, adding more
screen layouts will require also adding the corresponding movement code
to your activity, and this can become time consuming.
BE CAREFUL
Debugging layout issues across many linked layout files can be exhausting (I’ve
done it), so keep your layouts as stretchy and dynamic as you possibly can. If your
designs are done well, they should be able to automatically handle many screen
resolutions with good use of linear and relative layouts. Fall back on includes and
multiple layout folders only when dynamic layouts can’t do the job. There will be
times, however, when one layout doesn’t do all screens justice. When this happens,
make your breakouts as small and efficient as possible. Don’t hesitate to use this
amazing layout tool, but be careful not to use it too much.
Your Android application may, in a lot of cases, require some very specific hard-
ware in order to work correctly. I imagine that users who, for example, download a
camera app to a device that doesn’t have a camera will have a very poor experience.
<uses-feature android:name=”android.hardware.camera”
android:required=”true”/>
This line tells Android that the application should not be installed on a device
without a camera, because it’s required for correct operation. You can, on the flip
side, declare that your app use a particular piece of hardware, but degrade appro-
priately if it’s not there. An image-editing app might want the camera, but if the
camera’s not there it may still function by modifying images saved from the web
in the device’s built-in gallery. You tell the system this by declaring the hardware
as used but setting the requirement to false:
<uses-feature android:name=”android.hardware.camera”
android:required=”false”/>
There are a host of hardware features you can set. It’s probably best to check
the documentation for the full list (http://developer.android.com/guide/topics/
manifest/uses-feature-element.html).
You can add minimum and maximum supported SDKs if there are classes or
objects you rely on that aren’t available on older devices. You can, however, block
out older and newer versions of the SDK with a declaration that looks like this:
<uses-sdk
android:maxSdkVersion=”10”
android:minSdkVersion=”6” />
This will tell the Android Market to list the associated application for devices
that are SDK 6: Android 2.0.1 update 1 and above. It will also block devices greater
than Gingerbread from running your software. Further, if you try to load the app
through a web link, the downloader will block the install on the grounds that the
application isn’t supported.
It’s worth mentioning that this sort of heavy-handed blocking should really
be a last-ditch effort. If you can make your application work well with both the
latest and oldest devices, you should. With this declaration, you can limit who is
allowed to install your app.
I can’t tell you how many times I’ve found the perfect Android SDK class to solve
some annoying problem, only to find out that its use is limited to the latest version
of the SDK. There is one trick you can use when faced with code that will compile
only on later versions of Android: reflection.
While reflection is in no way unique to Android (it’s built into Java), it is some-
thing you can use to protect older phones from newfangled classes and methods.
This works just fine, but as it turns out, commit writes to the disk, and calling
this on the main thread is a no-no (for reasons we’ve discussed at length). In SDK
version 9, however, Google introduced the apply method to the SharedPreferences
Editor class. Again, this is great, but there’s a catch: Any device that tries to use a
class containing the apply method will throw a validation exception and crash. So
how, you might be wondering, do I use apply on Android SDK 9 (2.3.3) and higher
without breaking any 2.2 (or earlier) devices?
While this method starts the same as the previous one—getting the Editor and
using it to save the string—it diverges when it comes time to save that username.
In this chapter, you learned how to handle diversity in screen resolution, density,
and configuration. You did this through advanced use of the layout folders, the
<include> tag, and Android’s XML layout system. Then you learned how to tell
Android which device features you require by putting declarations in the manifest.
Last, you learned about using reflection to take advantage of advanced methods
when they’re available and to avoid them when they’re not.
Given all these tools, you should be ready to bring your killer mobile application
into play on the tremendous number of devices—from refrigerators to phones to
televisions—available to you on the Android platform.
In any case, monotony is boring. Different devices allow for innovation, greater
user choice, and funny-looking screen protectors. Now that you’re equipped to
handle it, you’ll be scaling resources and rocking the landscape mode with ease.
WRAPPING UP 185
8
MOVIES AND MUSIC
Support for media, both audio
aud
dio and visual, will
wiill
hopefully be an essential
al part of your next immersive
Android application. Both
h industries are ripe for reinven-
tion, and the next media format revolution almost certainly will
involve mobile devices. To this end, I’ll get you started with the
basics for Android’s video and music libraries in this chapter. I’ll
also point out a few things you’ll need to be aware of as you build
a background media playback service: movies, music playback,
background service, and what to watch out for.
187
MOVIES
Movie playback on an Android device boils down to the VideoView class. In this
section, I’ll use a simple example application that will play through every video
saved on a phone’s SD card. Here is the general process:
䊏 I’ll use the ContentProvider (something you’ll remember from our brief
discussion in Chapter 6 when we uploaded the most recent photo) to request
every video saved to the user’s external card.
䊏 After loading a Cursor (Android’s query result data object) with all the
device’s videos, I’ll need a method to play the next one.
䊏 I’ll set up an activity as a listener so that when video playback is complete, I can
call my playNextVideo method and move on to the next video in the cursor.
䊏 Last, I’ll clean up after my cursor when the user leaves the playback screen.
ADDING A VIDEOVIEW
Placing a VideoView onscreen is as simple as adding it to your XML layout. Here’s
what my main.xml file now looks like:
Here I’m setting the content view, retrieving and caching the video view with
findViewById, and setting my activity as the video view’s onCompletionListener.
In order for the activity to pass itself into the VideoView as the onCompletion
Listener , I have to extend OnCompletionListener and implement my own
onCompletion method. Here is what I’ve added to my activity:
MOVIES 189
I now have a configured, yet very simplistic, video player. You’ll most likely want
to have visual onscreen controls. Android’s VideoView allows you to implement and
set up a MediaController for the VideoView class. If you’re looking to go further
into video playback after this chapter, this would be an excellent place to start.
Android will, under certain conditions, search your SD card for new media
by using a helper called the MediaScanner. When new media is found, it’s
added to Android’s media ContentProvider. Using the columns defined in the
MediaStore class, you can query the phone’s built-in media library for con-
tent. The MediaScanner will read ID3 tags and any other metadata it can to
acquire information about the device’s content. Although this example and
the photo lookup example from the previous chapter are pretty simple, the
media store and Android’s ContentResolver can be powerful if used correctly.
Check the SDK documentation for these classes to get more information.
As you can see, I’m still fetching and caching the video view, but now I’m issu-
ing a query to Android’s ContentProvider for the DATA column of all media rows
that are videos. Specifically, you can see this in action in the highlighted code. That
query is fairly simple in that I want all videos on the external drive (SD card), and I
only care about the data column for all those rows. This column for any particular
row should always contain the path to the actual media content on disk. It’s this
path that I’ll eventually hand off to the VideoView for playback.
Note that it is possible to pass URIs to the video view. The video playback
mechanism will find the path to the object for you. I would, however, like to show
you the harder way so that you’ll be more informed. And if you later need to upload
or manipulate a file directly, you’ll know how to acquire it.
The Cursor object (a class Android uses to wrap database query responses) can
be null if the external media card is removed (or mounted into USB storage mode),
so I’ll need to check for a null cursor or one with no results before moving on.
Typically in this case, I’d display a message to the user about their SD card being
unavailable, but I’ll leave that task up to your imagination.
Last, I’ll get and cache the column index for the data row. This will make it easier
for my playNextVideo method to interact with the cursor’s results.
MOVIES 191
LOADING AND PLAYING MEDIA
At this point, you have a video view, a cursor full of media to play, and a listener
configured to tell you when media playback is finished. Let’s put the final piece
into the game, the code in playNextVideo:
My first task is to move the cursor to the next piece of media and make sure we
haven’t run out of stuff to play. When I know I’ve got a valid row from the cursor,
I can tell the video view what it should render next. Video views can accept both
a path defined as a string as well as the URI for a piece of media declared in the
content provider. As I mentioned earlier, the data column of the cursor contains
the file path to the media itself. I’ll pull this out of the Cursor, hand it off to the
video view, and then start playback.
TIP: You’re not limited to just file paths—you can hand the
video view a URL, and it will query and play the media found there.
@Override
public void onCompletion(MediaPlayer mp) {
playNextVideo();
}
At this point, the pieces are in place, videos play, and you’re almost done!
CLEANUP
You’ve seen me do this at least once before, but it’s always important to close any
cursors you request from the content provider. In past cases, I’ve requested data
with a query, pulled out the relevant information, and immediately closed the
cursor. In this case, however, I need to keep the cursor around for when the video
change has to occur. This does not get me off the hook; I still need to close it down,
so I’ll need to add that code to my activity’s onDestroy method:
@Override
public void onDestroy(){
if(mediaCursor!=null){
mediaCursor.close();
}
}
MOVIES 193
THE REST, AS THEY SAY, IS UP TO YOU
I’ve shown you the very basics of loading and playing video content. Now it’s time for
you to explore it on your own. Think about loading a video from a remote location
(hint: encoding a URL as a URI) or building a progress bar (hint: getCurrentProgress
calls on the VideoView).
Because errors are to media playback as swearing is to sailors, registering for
an onErrorListener is never a bad idea. Android will, if you pass it a class that
implements the OnErrorListener interface, tell you if it has hiccups playing your
media files. As always, check the documentation for more information on playback.
Just because you’ve run out of media to play doesn’t mean your player drops
into the idle state. It will keep the current file loaded if you want to call start
(which will restart the audio from the beginning) or seek (to move the playhead to
a particular place). Only when you call stop or reset does the MediaPlayer clear
its buffers and return to the initialized state, ready for you to call prepare again.
MUSIC 195
PLAYING A SOUND
At its most straightforward, media playback is actually quite easy. Android gives you
helper methods to shepherd your media player from the idle state to the prepared
state if you can specify a file or resource id right away. In this example case, you
can record your own WAV file or use the beeeep file that I included in the example
project. I’ve added the file to the newly created /res/raw/ folder so the application
can load it directly.
Further, I’ve added a button (which you should be a pro at by now) that, when
pressed, will play the recorded audio. Once the button is defined (id beep_button)
in the main.xml layout file and the audio beeeep.wav file is placed in the raw/ folder,
the following code should work like a charm:
MediaPlayer mBeeper;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button beep = (Button)findViewById(R.id.beep_button);
beep.setOnClickListener(this);
mBeeper =
MediaPlayer.create(getApplicationContext(), R.raw.beeeep);
}
As you can see, I’m retrieving the beep_button from the main.xml layout (which
I told the activity would be my screen’s layout) and setting my activity as the click
listener for the button. Last, I use the media player’s create helper method to initial-
ize and prepare the media player with the beeeep.wav file from the raw/ directory.
@Override
public void onClick(View v) {
mBeeper.start();
}
CLEANUP
In order to be a good citizen, there’s one more step you need to remember to take:
releasing your resources! That’s right, when your activity closes down, you need
to tell the media player that you’re finished with it, like so:
@Override
public void onDestroy(){
if(mBeeper != null){
mBeeper.stop();
mBeeper.release();
mBeeper = null;
}
}
Checking for null before performing the cleanup is a good precaution. If, for
whatever reason, there isn’t enough memory to load the resource or it fails for
another reason, you won’t have any null pointer exceptions on your hands.
MUSIC 197
LONGER-RUNNING
MUSIC PLAYBACK
You didn’t think I’d let you off that easy, did you? Remember two chapters ago
when I showed you how to build a service in a separate process by using an AIDL
file? I told you you’d need it for longer-running music playback. Here’s a quick
recap of that process:
1. Create a service, define a few methods to control music playback, and declare
the service in your manifest.
3. Bind an activity to the service, and, when the callback is hit, save the binder
in order to call the service’s methods.
If most, or any, of those steps don’t make sense, take a gander back at Chapter 6.
In this section, I’ll show you how to turn the empty service into one that actu-
ally plays music in the background. The example music service will have methods
to pause, play, set a data source, and ask it what the title of the current song is. To
show you this service in practice, I’ll have my activity play the most recently added
song on my new background music service.
In this code, I’m using a cursor loader to fetch my rather bizarre query. I’m ask-
ing the content provider for all possible audio tracks, but I’m sorting the results
in descending order of their addition date (that is, when they were added to the
phone) and limiting it to one result. This will, when the loader finishes, return a
cursor with one record (the most recent song added to the library).
Android, in version 3.0, added the Loader class to its arsenal of helpers. A
loader is essentially a simplified version of the AsyncTask class. It processes
or acquires a piece of data off the main thread and then calls your listener
on the main thread when the data is ready. In this chapter, I’m giving it a
query string that, if the user has a lot of media, could take a very long time.
This is an excellent time to deploy one of Android’s loader subclasses: the
CursorLoader. You can user this library on versions of Android older than 3.0
by including the Android compatibility library in your project. To add it, right-
click your project and select Android Tools > Add Compatibility Library.
@Override
public void onLoadComplete(Loader<Cursor> loader, Cursor cursor) {
if(!cursor.moveToFirst()){
Toast.makeText(getApplicationContext(), “No Music to Play”,
p Toast.LENGTH_LONG).show();
return;
}
int idIDX = cursor.getColumnIndex(MediaStore.Audio.Media._ID);
long id = cursor.getLong(idIDX);
if(mService == null){
Toast.makeText(getApplicationContext(),
“No Service to play Music!”,
Toast.LENGTH_LONG).show();
return;
}
try{
mService.setDataSource(id);
mService.play();
When the loader hits my callback, I’ll first need to check if it actually found any
data. By checking if(!cursor.moveToFirst()){, I’m moving to the first and only
record in the cursor, but I’m also making sure there actually is a record for me to
look at. (If the cursor is empty, moveToFirst will return false.)
Next, I’ll get the column index of the _ID column and call getLong on the cursor
to acquire the media’s unique ID. It is with this ID that I’ll tell the music service
what it should play.
I’ll also need to make sure that my service bind in the onCreate method was
successful. Once I know that the service is valid, I can tell it what entry I want it to
play with setDataSource and then tell it to start playback with play.
Cursor mCursor;
MediaPlayer mPlayer = null;
public void setDataSource(long id){
if(mCursor != null){
mCursor.close();
}
mCursor =
While this code is a little bit long, most of it should look similar to tasks you’ve
already done.
1. I’m querying the content provider for the id passed into the method.
2. I’m making sure that the music is actually there first by checking if the cur-
sor came back null (which can happen if the SD card has been removed).
I’m also checking that there’s a valid row in the cursor.
It’s worth noting, again, that I’ve taken the harder of two routes here.
Instead of querying the content provider for the exact media path, I could
build a URI for the media in question and hand it off instead. I’ve taken what
may be a slightly more complex route to playback only so that when you’d
like to get to the file itself, you’ll know how.
3. When I’m sure the cursor is valid and contains the data for a song to play, I
can reset the player (in case it was already playing something else), set the
data source for it, and tell the media player to prepare. Once these methods
are done, the media player is ready to start playback.
With that, your service is ready to go when the activity calls play.
PLAY TIME
Now that the service has a data source and is prepared, the activity can call play,
which will trigger the following code to run:
You’ll need to start media playback and make sure the service switches to run-
ning in the foreground. setForegroundState is a method I defined back in Chapter 6
that places an icon in the notification screen. If you need a refresher on how to
put services into foreground mode, review Chapter 6 or look at the sample code
for this chapter.
mPlayer.setOnCompletionListener(this);
You can call it at any point after the player is created. Of course, your service
will need to implement OnCompletionListener and have the correct onCompletion
method.
@Override
public void onCompletion(MediaPlayer mp) {
stopSelf();
}
This means that once the media is finished, the service will call stop on itself,
which, because of the lifecycle of the service, will trigger Android to call the service’s
onDestroy method—the perfect place to clean up. Once the cleanup is finished,
the service will be deallocated and cease running.
CLEANUP
Cleanup is essential when dealing with cursors and media players. If you don’t
handle this section correctly, a lot of the device’s memory can get lost in the shuffle.
Here’s the onDestroy method where I clean up both the cursor and the media player:
@Override
public void onDestroy(){
super.onDestroy();
if(mCursor != null)
I must be careful, because an incorrect data source ID or bad media file could
leave either of these pointers null, which would crash the service quite handily
when I try to shut them down.
INTERRUPTIONS
When you’re writing music software for Android devices, it’s vitally important that
you remember that the hardware on which your software is running is, in fact, a
phone. This means you’ll need to watch out for several things.
䊏 Audio focus. You’ll need to use the AudioManager class (introduced in
Android 2.2) to register an audio focus listener, because other applications
may want to play alerts, navigational directions, or their own horrible music.
This is vital to making an Android music playback application play nice
with the rest of the system.
䊏 Controls built into headphones. You’ll want your service to register to receive
headset button intents through your manifest (or at runtime when your
service is started). At the very least, set your service up to pause when the
headset control is clicked.
䊏 Phone calls. By watching the phone’s call state either through the Telephony
Manager or with the audio focus tools, you absolutely must watch for incom-
ing phone calls. You must stop all audio when the phone rings. Nothing will
enrage your users (and hurt your ratings) more than not accommodating
phone calls.
This might seem like a lot of things to look out for (and it is), but never fear, the
developers at Google have released an open source media player (which they ship
with the Android source code) that can be a great guide for dealing with this stuff.
As always, the documentation will have a lot on the subject as well.
You should now be comfortable with the essentials for media playback. If you’re
looking to go further with videos (which I hope you are), you’ll want to look into
using a controller to modify the state of the video view.
Your next step to expand the media playback service is to think about how you’d
pass arrays of IDs (playlists) and how you’d deal with updating those playlists on
the fly (as users change them).
Android can be a very powerful media platform if you’re careful and treat it
with care. Go forth and make a crop of better music players—if for no other reason
than so I can use them myself.
WRAPPING UP 207
9
DETERMINING
LOCATIONS AND
USING MAPS
One of the chief benefits off building
bu
uilding any mobile
mobiile
application is the ability to
o provide
rovide location-aware data to
users. Android is no exception.
eption. Taking advantage of your
user’s location to help them make informed decisions should
always be in the back of your mind. There is, however, a little
to know about the basics of determining and using the device’s
location. I will show you a few tricks for speedy acquisition and
then quickly show you how to display Android’s built-in Google
Maps view.
209
LOCATION BASICS
MOTHER MAY I?
If you want to get the location of a user’s device, you’ll need to add the location
permission to your manifest. Depending on the level of location data you want to
acquire, you’ll need to declare one of the following permissions:
<uses-permission
android:name=”android.permission.ACCESS_COARSE_LOCATION”/>
<uses-permission
android:name=”android.permission.ACCESS_FINE_LOCATION” />
The <uses-permission> tag should be declared inside the manifest but outside
the <application> section.
locationManager = (LocationManager)getSystemService
p (Context.LOCATION_SERVICE);
Typically, I’ll stash this pointer away somewhere so I never have to find it again.
The method I’m most interested in, in this case, is the onLocationChanged
method. It will pass me that all-important location object. With that data, I can
then call getLatitude and getLongitude. And with that, I know—with as much
accuracy as possible—where in the world the device is.
Further, the LocationManager object contains an important static helper
method called distanceBetween that will calculate the distance between geo-
graphic points. I point out this helper because I find myself using it all the time.
THAT’S IT!
As much as I would like to say that this is an incredibly complex operation, it’s
about as hard as tying your shoelaces. Given what you’ve been through up to this
point, getting the location from the LocationManager should be a cakewalk. That
said, I had my fair share of issues in writing the code for this chapter. If you are
stumped, check the documentation and press on!
Determining your user’s location is one thing, but actually putting those two inde-
cipherable numbers (longitude and latitude) into context is where software gets a
little more complex. Interestingly enough, the configuration needed to get a map
onscreen is far more complex than the code to manipulate it. So, let’s get started.
If you want to follow along, go ahead and create a new Android project, and we’ll
start from there.
Now, with that out of the way, it’s time to switch your boring Activity to a
bigger, sexier MapActivity class.
CREATING A MAPVIEW
This is the view into which Android will draw its map tiles. It behaves exactly like any
other view, with one notable exception: You need an API key to access Google Maps.
As you can see, I’ve placed my map view as the only one onscreen. You can
actually place it anywhere you want, just as you would position any other view.
Further, it doesn’t require, as the ListActivity does, a special reserved Android
ID. Let me show you where to get the value that you’ll place in the apiKey field.
From the sign-up page, you must agree to their terms (which you should prob-
ably read first), enter the output from your keytool command, and get your API
key. Toss that string into the apiKey section (the highlighted line in the main.xml
code) of the MapView, and you’re good to go. Remember, the map key is bound to
whatever key you signed your .apk with (in this case, the debugging one). When
you sign your .apk for release, you’ll need to remember to generate a new key for it.
MapView mv;
MapController controller;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mv = (MapView)findViewById(R.id.map_view);
controller = mv.getController();
GeoPoint point = new GeoPoint((int)(40.734641 * 1e6),
p (int)(-73.996181 * 1e6));
controller.animateTo(point);
controller.setZoom(12);
}
The location listed in the example happens to be New York City (where I’m
currently writing this book), and I’ve set the zoom level high enough that you can
almost see my house—all right, maybe not! Figure 9.2 shows what all your hard
work thus far has yielded.
Google, in its not-so-infinite wisdom, decided that GeoPoints for the map
view should not accept latitude and longitude values in degrees like every
other API in the SDK. Instead, they take them in 1e6 values. This means,
simply, that you must multiply any latitude or longitude value you wish to
reference in the map view by 1e6 before handing off. Interesting map errors
will result if you forget this step.
WRAPPING UP
In this chapter, I showed you the very basics for finding a device’s location and
displaying a map onscreen. If you’re looking to go further into this topic, you should
explore map overlays through the OverlayItem class. Overlays let you point out
specific locations (addresses, businesses, or cat pictures) to your users.
As always, be very careful what kind of location services you use, especially
while the user is not in your application. Nothing will drain a user’s battery faster—
and make them angrier—than heavy locational lookups in the background. If
you’re planning a very location-heavy application, be sure to do LOTS of battery-
draw testing before you release it. Your users and your application’s ratings will
be much happier for it.
WRAPPING UP 219
10
TABLETS,
FRAGMENTS, AND
ACTION BARS,
OH MY
With the release of Honeycomb
ycomb and Ice Cream
m
Sandwich, Google has introduced
oduced
duced a totally new form factor
to the Android landscape.
pe. In Gingerbread and earlier,
Google had sought to handle only small handset screens. With
these new versions of Android, you now must deal with the much
larger screens of tablets.
221
FRAGMENTS
Fragments, conceptually, are like activities with a slightly more complex lifecycle.
They can be given a screen to themselves if there isn’t much room, or they can be
placed with many other fragments on a larger tablet screen. An activity can contain
any number of fragments. In this way, the Android SDK allows you to expand and
collapse the views in your application to take advantage of more and less screen
space. There is one thing that the activity can do that the fragment cannot—namely,
the activity can register for intents in the manifest; fragments rely on their host
activity to pass on launch information. Further, it’s important to implement frag-
ments such that they are totally unaware of what other fragments are visible. This
becomes important, because you’ll want to change that configuration depending
on how much space you have.
If you’re planning on coding along with me in this chapter, make sure you have a
project that is set to version 3.0 or higher of the Android SDK (API Level 11 or greater).
FRAGMENTS 223
CREATING A FRAGMENT
To create a fragment, you’ll need to make a Java class that extends the Fragment
class. An incredibly simple implementation would look something like this:
Fragments, of course, need their own layouts to show anything onscreen. This
ContentFragment class will just show a simple text view. Here is the content_
layout.xml file whose contents will be drawn as the fragment itself:
Keep in mind, however, that the getView method works only after you’ve
returned from onCreateView. While you now have a fully functioning fragment,
you still need to make it appear onscreen.
SHOWING A FRAGMENT
There are two main ways one can make fragments appear onscreen.
USING XML
You can declare a fragment in an XML layout to make the fragment appear onscreen,
like so:
File: res/layout/content_activity_layout.xml
<?xml version=”1.0” encoding=”utf-8”?>
<LinearLayout xmlns:android=”http://schemas.android.com/
p apk/res/android”
android:layout_width=”fill_parent”
android:layout_height=”fill_parent”
android:orientation=”vertical” >
<fragment android:name=”com.haseman.fragments.ContentFragment”
android:layout_width=”match_parent”
FRAGMENTS 225
android:layout_height=”match_parent”
android:id=”@+id/list_fragment”/>
</LinearLayout>
This layout can then be set as the content view for a FragmentActivity, like so:
Figure 10.1 shows the results of using XML to make the fragment appear
onscreen.
The FragmentActivty is a special class you’ll need to use only if you want to
work with fragments on versions of Android earlier than 3.0. For Honeycomb
and Ice Cream Sandwich, a simple activity contains all the pieces you need
to interact with fragments. You can find the FragmentActivity class in the
Android compatibility library.
FRAGMENTS 227
Fragments, when set up this way, can be placed onscreen the same way as views.
Here’s an XML layout to show a list view with the text view next to it:
I’ve used a linear layout and some weighting in the fragments to give the Demo
ListFragment the left one-third of the screen and the ContentFragment the right
two-thirds. (If you’re wondering about DemoListFragment, you can find it in the
sample code for this chapter.)
Figure 10.2 shows what it looks like on a tablet.
FRAGMENTS 229
The variable containerViewId should refer to an existing ViewGroup in your
activity’s layout where the new fragment should be placed. You can also, at a later
time, replace one fragment with another by calling replace(containerViewId,
newFragment);, where containerViewId specifies the view container that cur-
rently holds the fragment you’d like to replace. You can replace only fragments that
were previously added using a FragmentManager transaction; fragments declared
statically in XML layouts cannot be replaced.
By using either XML or the fragment manager to set up and modify views, you
should have no trouble building complex, scalable, and beautiful applications that
render well on both handsets and tablets.
Remember that all fragments should work independently of their siblings
(much in the same way that activities should stay independent), even if they might
share the same screen.
Given the power of Android’s layout folders (which we covered at length in
Chapters 3 and 7), you should see the possibilities in building one layout for tablets
(which could have several fragments on it) and building another for small-screened
phones (which would have only one visible fragment at a time).
If you’re looking for a simple example, I highly recommend you take a look at
the project in the sample code for this chapter.
import android.support.v4.app.Fragment;
import android.app.Fragment;
Using the support library will ensure that your application will run correctly
on newer and older systems alike.
Further, if you’re planning on using the compatibility library and fragments,
remember that you’ll need to use a FragmentActivity instead of a regular Activity.
With the compatibility support and the dynamic nature of fragments, it becomes
quite possible to create an application with a great interaction model that works
well on both phones and tablets. I don’t have time to spell it all out here, but there
is sample code for achieving this in the companion source code for this chapter.
Remember what you’ve read here, and take a look through it.
FRAGMENTS 231
THE ACTION BAR
With the transition from Android 2.3 to 3.0, Google has eliminated both the search
button and the menu key. From personal experience, I can tell you that many new
users never find functionality that is placed in the options menu. Its removal from
the system is, indeed, a very good thing.
Google, bless their expensive cotton socks, has moved the icons that used to
reside in the options menu to the action bar. Further, you can specify a search view
in the action bar (to replace the search button). This takes up more screen space,
but on a tablet (and on later phones), there is more than enough space to go around.
The action bar now represents the primary way your users will navigate through
your Honeycomb or Ice Cream Sandwich application. Sadly, this new tool is avail-
able only through versions 3.0 (target 11) or later. It would require a fair amount of
typing, but it’s very possible to emulate the action bar on earlier systems by using
a simple linear layout and a few image views. You are, however, on your own to
implement it. There is no support for the action bar in the compatibility library.
<uses-sdk android:minSdkVersion=”7”
android:targetSdkVersion=”11”
/>
At this point, when your project is built and run on any Honeycomb and later
system, you should see a basic version of the action bar containing your applica-
tion’s icon and the default title of the activity (Figure 10.4).
Further, Android will always make your application icon (farthest to the left)
clickable. It is expected that tapping this icon will, by default, return the user to
your application’s home screen (whatever this means to your application’s behav-
ior). You can also visually indicate that the home icon will go one level back in the
activity stack by calling setDisplayHomeAsUpEnabled(true).
The way you make changes to what’s in the action bar is very similar to how
you used to interact with the options menu. This is not by accident. Because the
action bar is supposed to replace the options menu, you call methods and configure
files similarly to how you used to deal with the menu. This also makes it easy to
gracefully degrade service to phones on older versions of the Android SDK.
ADDING AN ICON
Icons are most easily placed in the action bar by extending onCreateOptionsMenu
and adding the menu icons you’d like. I’ve added a delete icon to the action bar
with the following code:
@Override
public boolean onCreateOptionsMenu(Menu menu){
MenuItem item = menu.add(“delete”);
item.setIcon(android.R.drawable.ic_delete);
if(Build.VERSION.SDK_INT >= 11){
item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
}
return true;
}
ADDING A TAB
Placing a tab in the action bar is a totally different process. You’ll use the ActionBar
class itself to add them. Further, you’ll need to implement an ActionBar.Tab
Listener (it tells the system, through a series of overridden methods, what to do
when the tab is tapped by the user).
Once you’ve implemented your listener, you should then add the following
code to your activity’s onCreate method:
For each tab you’d like to add, you must go through the process of request-
ing a new tab from the action bar, setting the listener, and then adding it to the
action bar. Figure 10.7 shows a great-looking example of a tab bar in the Google
Calendar application.
Each tab will trigger different events on the fragments within an activity. For
the Google Calendar application, it will hide and show the various ways in which
the user could view their calendar.
As you can see, Google has been very busy building new user interface paradigms
for tablets in Honeycomb. Once they settled on how to make the tablets work, they
added those new UI methods to the telephone world with Ice Cream Sandwich.
Using the action bar, Google was able to do away with two hard menu but-
tons (options menu and search), while keeping those concepts active in the user
experience. The options menu button was replaced by icons added to the action
bar, and the search button was replaced by the ability to add custom action views
to that very same bar.
With fragments, Google has enabled us to place more or less on the screen as
the available real estate shifts between devices. Through fragments, we are no
longer limited to having one thing on the screen at any given time. However, if
we need to handle smaller screens, fragments make it easy to shift back into the
one-thing-per-screen layout.
Fragments and the action bar are very new concepts, and it’s clear that Android
developers are still trying to figure out how best to use them. Please, help us to
push the mobile user experience forward by implementing your own killer user
interfaces using these new toys Google has given us.
WRAPPING UP 237
11
PUBLISHING YOUR
APPLICATION
For the most part, the Android
ndrroid Market is on
one
ne
of the easiest-to-use application
lication
ation stores I’ve ever encoun-
encoun-
tered. But although you can update an application almost
instantly without any of the hassles of other app stores, there are
still a few things you should be aware of before you publish. We’ll
cover packaging, versioning, and creating a release build.
239
PACKAGING AND VERSIONING
There are a few key points in your manifest that you need to pay attention to before
you consider producing a release build to go to market. You’ll need to make sure
your application isn’t debuggable (not so much an issue with newer versions of
the Android client). You’ll also want to make sure your package name is unique
and consistent in each subsequent version. Last, there are two fields to pay atten-
tion to when upgrading an existing application. Let’s take a closer look at all three.
PREVENTING DEBUGGING
Shipping your application out the door with debugging enabled will allow anyone
with a micro USB cable to step through lines of code in your app, look at the con-
tents of variables, and do other things that no security-aware engineer would like
to have happen. The debugging flag is turned off by default, but if you’ve turned
it on, it will appear in the application portion of the manifest:
<application
android:icon=”@drawable/ic_launcher”
android:label=”@string/app_name”
android:debuggable=”true”>
When the application ships, make sure that you either remove the line entirely
or set the following:
android:debuggable=”false”
package=”com.sparkle.pants.fairy.dust.unicorn”
VERSIONING
There are two values to pay attention to when updating an existing application. First,
you should (but are not required to) increase the value inside the versionName field of
the manifest declaration. Standard rules for the version number apply: Major releases
get a new primary number (1.0 to 2.0), while small patches should get a secondary
bump (1.0 to 1.1). The version name is what shows to the user in the Android Market
and in your application’s details screen. The version name is “1.0” in the previous
example’s manifest file.
The field you must pay careful attention to is versionCode. This is the value
that must change every time you do an update for the Android Market. Sending an
update to the Android Market will be rejected unless you change the versionCode.
Typically, Android developers will make the version code by taking the periods out
of the version name and padding each portion of the name to create a two-digit
number for each section. The number must be unique, but it does not necessarily
have to be sequential. So version 1.0.1 would become 010001, and 2.3.12 would
become 020312. This is a pretty basic way to make sure your version names stay
tied to the version code without much complexity. It’s a good idea to make this
number constantly grow even though, according to the documentation, it isn’t
technically required to. However, adopting a convention of incrementing the num-
bers ensures it will be unique.
The number in minSdkVersion corresponds to the integer value for the SDK. In
this case, by declaring version 7, I’m not allowing phones earlier than the Android
2.1 update 1 (which is SDK version 7) to install my application. Be sure to test your
application on the versions you support, even if you just test it briefly with an
emulator.
So your version number is sorted, your code is tested, and all your resources are
in place—it’s time to make a release build before submitting.
There are two ways you can go about producing your final APK: through Apache’s
ant (which will build your application from the command line in conjunction with
the build.xml file) or through Eclipse’s Android tools. If you’re comfortable with
the command line, I’m going to assume you can create a release build on your own.
In this chapter, I’ll focus on creating your release build through Eclipse.
Before you ship anything, run through your code base and make sure there
are no extra logging lines that you don’t want anyone with a micro USB
cable to see. Remember, too, that applications with the right permissions can
view log output. That said, shipping your application with some logging in
place for catastrophic errors isn’t that bad an idea (in case there are failures
in the field).
You’ll be asked if you want to use an existing keystore file or create a new one.
Because this is your first time releasing your product, you’ll need to create a new
one. Enter a location for the file, or click the Browse button to find one. Enter a
password, and re-enter it to confirm that it is correctly typed (Figure 11.2). The
keystore is a file that can contain any number of keys. For your purposes, you’ll
only really need one key. All your applications can be signed with the same one,
or you can use different keys—it’s up to you (but I recommend using only one,
because it’s less to keep track of).
REMEMBER TO ZIPALIGN
For you command-line users, one thing that Eclipse is doing for its users
is a tool called Zipalign (found in the tools/ folder of the SDK). Zipalign
decreases your application’s load times. If you’re using the Eclipse tools, it’s
already happening for you automatically. If you aren’t using Eclipse, make
sure you take this step on the command line after you’ve signed the build.
It’s speed for free, so make sure you take advantage of it.
At this point, it’s time to sign up for a developer account and submit your build.
Android’s application submission page is fairly self-explanatory, but I should point
out that it’s important to provide the Market with as many screenshots, videos, and
graphical assets as you have time to generate. Making the decision to purchase an
application can, if you can believe it, be a difficult one. Users need to be able to
trust that your application actually works as advertised, so giving them a sneak
peek is essential.
UPDATE FREQUENTLY
Your application, after you submit it, will show up in the Market within hours. This
allows you to frequently update in order to add small features, fix bugs, and make
small tweaks. No other platform allows you this kind of speed from submission
to availability. Use it. You’ll be amazed by how grateful your users will be if you
respond to their problems quickly.
In parting, I want to give you one more piece of advice: Make a meaningful contribu-
tion to the Android landscape. While you’ll undoubtedly have questions that this
book cannot answer, you now have the vocabulary and knowledge that will allow
you to find answers. This means you have no excuse but to make something amazing.
Please—the Android Market is, for lack of a better phrase, full of crap. The world
doesn’t need another flatulence app; we need things that make data more accessible,
meaningful, fun, useful, and interesting. Do not build apps, build applications.
Good luck, and happy hacking.
WRAPPING UP 247
INDEX
248 INDEX
Android SDK Manager B
described, xv
binder service communication, 160–165
locating, xv
binder and AIDL stub, 162–164
using, 6–7
creating services, 161–162
Android Virtual Device (AVD), configuring, 11–12
bitmaps, fetching and displaying, 100–101
AndroidManifest.xml file
BroadcastReceiver
<manifest> declaration, 22
creating for intents, 41–43
package definition, 22
registering, 42–43
android:name tag, 23
self-contained, 44
ANR crashes, tracking down, 102
builds
ant install command, 16
crash reports, 246
API levels, monitoring, 184
submitting, 246
APK file, watching size of, 76
updating, 246
Application class
button bar layout, 87
accessing, 50–51
button_layout.xml file, creating, 172, 174–175
accessing variables, 51
buttons
activities, 49
adding to services, 152
adding data to, 50
layout XML, 170–171
customizing, 48–50
default declaration, 48
getApplication method, 50 C
applications
cache folder, creating for images, 115
minimum SDK value, 242
call state, watching, 205
names, 48–49
cd command, 16
preventing debugging, 240
classes
updating, 241
Activity, 25–27
apps, limiting access to, 180–181
imageReceiver, 119–120
ArrayAdapter class, creating and populating, 131–132
Intent, 37
AsyncTask class, 106–112
Loader, 121
avoiding use of, 112
click events, reacting to, 133
doInBackground method, 106, 109
click listeners
keeping track of, 111
adding to buttons, 65
onPostExecute method, 106, 111
calling for views, 62
onPreExecute method, 106, 109
registering with views, 63
publishProgress method, 110
setting, 65
showing progress, 110
colon (:), using with services, 161
starting within activities, 111
command line, creating projects from, 16
type arguments, 108–109
communication. See also services
using, 111–112
binder service, 160–165
audio, playing in services, 201–204
intent-based, 150–159
AVD (Android Virtual Device), configuring, 11–12
compatibility library, using with fragments, 230–231
content observer, registering, 154
INDEX 249
ContentFragment class, 224–225 locating Android SDK, 9–10
ContentObserver, using with services, 158 Zipalign tool, 245
ContentProvider emulator
cursor for, 159 creating, 10–13
registering observer with, 154 troubleshooting, 18–19
cursor loader, using for music playback, 199 exceptions, handling, 137
cursor.close, calling on cursors, 159 exporting release build
cursors release build, 243
closing for media, 193 signed build, 243–244
moving to media, 192 extended views. See also custom views; views
custom views. See also extended views; views changing colors, 67
adding to XML, 70 creating instances, 68
declaring class for, 65–66 customizing, 66–68
extending, 66, 68–69 ForegroundColorSpan, 66–67
using, 68–70
D
data, fetching and displaying, 100–101
F
DDMS (Dalvik Debug Monitor Server), xv file system cache, relying on, 116
perspective, opening, 17 files, 22
debugging directory, 23
layout issues, 179 locating, 23
preventing, 240 saving to SD cards, 116
dialogs, beware of loading, 110 folders, 22
drawable folders ForegroundColorSpan, using with extended views, 66–67
contents of, 71 FragmentActivity class, 226
referencing, 76 FragmentManager, 229–230
using, 76 fragments
backward compatibility, 230–231
compatibility library, 230–231
E
content view for FragmentActivity, 226
Eclipse IDE, xiv ContentFragment class, 224–225
adding Android plug-in to, 8–9 creating, 224–225
backing up keystore file, 244–246 declaring in XML layout, 225–226
creating activities in, 25–27 DemoListFragment, 228–229
creating emulator, 10–13 features of, 222
creating views, 55 layouts, 224–225
declaring services, 114 lifecycle, 222–223
downloading, 4 onAttach method, 222
exporting signed build, 243–244 onCreate method, 222
IMusicService.java file, 161 onCreateView method, 222
installing, 5 onDestroy method, 223
250 INDEX
onDestroyView method, 223 images
onDetach method, 223 cache folder, 115
onPause method, 223 downloading and displaying, 100–101
onResume method, 222 external storage, 115
onStart method, 222 fetching, 114–120
onStop method, 223 listener for result broadcast, 118–119
placing onscreen, 228–230 notifyFinished method, 118
showing, 225–230 rendering download, 118–120
text view, 225, 227 <include> tag, using for small changes, 172–176
installing
Android SDK for Linux users, 6
G
Android SDK for Mac users, 5–6
GeoPoints, using with maps, 219 Android SDK for Windows users, 6
getApplication method, 50 Eclipse IDE, 5
Google Maps library, 214, 216 Intent class manifest registration, 37–38
gray background, adding to RelativeLayout, 95–96 intent filters, registering for, 40
intent-based communication, 150–159
H auto image uploading, 150–151
declaring services, 151
hierarchy viewer, locating, xv
getting services, 151
Honeycomb
going to foreground, 155–157
action bar, 232
observing content changes, 158–159
action views, 236
spinning up services, 154–155
FragmentActivity class, 226
starting services, 152–154
FragmentManager, 229
intents. See also activities
Navigation, 232
adding, 38–40
SetShowAsAction, 234
BroadcastReceiver, 41–43
creating, 29–30
I features of, 37
Ice Cream Sandwich getting for activities, 31
action bar, 232 listening for, 41–45
FragmentActivity class, 226 listening for information, 43
Navigation, 232 moving data, 45–47
icon clicks, reacting to, 234–235 receivers, 41–43
icons, adding to action bar, 233–234 receiving, 37
image fetcher registering receivers, 42–43
handleIntent method, 116–117 retrieving and using strings, 46–47
implementing, 116–117 reviewing, 47
image uploading, automatic, 150–151 self-contained BroadcastReceivers, 44
ImageIntentService, 114 stopping listening, 43
imageReceiver class, 119–120 toasts, 42
INDEX 251
IntentService LinearLayouts, 82–89, 130
declaring services, 113–114 button bar layout, 87
fetching images, 114–120 layout of children, 84
nesting layouts, 84
orientation, 86
J
padding option, 88–89
Java versus RelativeLayouts, 84, 89
views in, 56–58 using, 87
versus XML layouts, 60 list element rows, recycling, 144
JSONArray object, using with list views, 139–140 List Fragment, 126
list views
K building, 141–142
custom layout view, 142–143
key, creating, 244–245
fetching data, 138
keystore file
getting Twitter data, 136–138
backing up, 244–245
getTwitterFeed, 138
creating, 244–245
getView code, 142
handling exceptions, 137
L interaction with Adapter class, 144
layout files, separating from activities, 178 JSONArray object, 139–140
layout folders, 170–176 JSONObject, 142
adding suffixes to, 177 ListActivity class, 135–136, 139–140
buttons, 170–171 main layout view, 134–135
contents of, 71, 75–76 onCreate method, 135
<include> tag, 172–176 TextViews, 142–143
MVC (Model-View-Controller), 75 ListActivity class, 139–140
specifying, 172 creating, 128–130
layout issues, debugging, 179 IDs, 129
layout-land folder XML layout file, 128–129
creating, 172 ListView class
defining screens in, 177–178 custom adapter, 138–140
layouts described, 126
AbsoluteLayout, 78–82 Loader class
button bar, 87 described, 121
height and width values, 55, 57, 86 using for music playback, 200–201
LinearLayout, 82–89 location service
nesting, 84 distanceBetween method, 212
RelativeLayout, 90–96 finding supplier, 211
ViewGroup, 77–78 getBestProvider method, 211
XML versus Java, 60 getLastKnownLocation, 213
LocationListener interface, 212
252 INDEX
LocationManager object, 212 Cursor object, 191
onLocationChanged method, 212 loading, 192–193
registering for updates, 211–212 moving cursor to, 192
using, 211 onErrorListener, 194
locations playing, 192–193
adding permission to manifest, 210 playNextVideo, 192
getting for devices, 210 searching SD cards for, 191
<uses-permission> tag, 210 media players
logging, disabling, 244 cleanup, 204–205
onDestroy method, 204–205
MediaPlayer states
M
Idle, 195
main menu Initialized, 195
ArrayAdapter class, 131–132 Playing, 195
click events, 133 Prepared, 195
data, 127 MediaScanner, 191
list items, 130–131 menu list items, text view file, 130–131
ListActivity class, 128–130 <merge> tag, wrapping views in, 176
main thread. See also thread violations methodNotFoundException, 184
being on, 102 Model-View-Controller (MVC), 75
fetching data, 100–101 movie playback
getting back on, 104–105 adding VideoView, 188–189
getting off, 103–105 cleanup, 193
Loader class, 121 closing cursors, 193
recommendations, 102 onDestroy method, 193
manifest, 22 process, 188
map key, getting, 217 setting up for VideoView, 189–190
MapActivity class music playback
availability of, 214 audio focus, 205
creating, 215–216 cleanup, 197, 204–205
MapControl class, 217–218 closing cursors, 204–205
maps crashing service, 205
manifest additions for, 214–215 cursor loader, 199
using GeoPoints with, 219 finding recent track, 199–201
MapView class headphone controls, 205
availability of, 214 icon in notification area, 203
creating, 216–217 interruptions, 205–206
testing, 217–218 Loader class, 200–201
value for apiKey field, 216–217 missing SD card, 206
media onDestroy method, 197, 204
ContentProvider, 190 phone calls, 205
ContentResolver, 191
INDEX 253
music playback (continued ) permission, adding to manifest, 210
playing audio in services, 201–203 phone’s call state, watching, 205
setDataSource, 201–202 photo listening service
setForegroundState method, 203 registering for media notification, 154
sounds, 196–197 starting, 153
stop method, 204 stopping, 153
music service, binding to, 198–199 photos, uploading, 159
MusicExampleActivity, 198 playNextVideo, 192
MVC (Model-View-Controller), 75 preferences, saving usernames to, 182
projects
creating, 14–16
N
creating from command line, 16
New York City, map of, 218 DDMS perspective, 17
NewActivity class, 39 Java package, 15
Next button, creating with RelativeLayout, 93–94 naming, 15
Notification object, creating for services, 156 naming activities, 15
notification pull-down, creating for services, 157 naming applications, 15
running, 17
O selecting, 14
selecting version of, 15
OnClickListener, using with views, 62–65
public void onResume method, using with activities, 33
onCreate method, 24
public void onStart method, using with activities, 33
ListView class, 135
using with views, 63
onDestroy method, using with activities, 34 R
onErrorListener, using with media, 194 reflection
onKeyDown method, using with activities, 28–30, 40 accessing SDK methods with, 183–184
onPause method, using with activities, 34 benefits of, 184
OnRetainNonConfigurationInstance method, 35 methodNotFoundException, 184
onSaveInstanceState method, 35–36 RelativeLayouts, 90–96
onStop method, using with activities, 34 gray background, 95–96
versus LinearLayouts, 84, 89
P Next button, 93–94
padding declaration, 93
packages
<RelativeLayout> declaration, 92
downloading, 6–7
using, 90–96
naming, 240–241
release build, exporting, 243
packaging
res/ folder
and signing, 243–245
contents of, 71
and versioning, 240–242
layout folders, 170–177
padding
resources, finding, 59
LinearLayouts, 88–89
RelativeLayouts, 93
254 INDEX
R.javafile notification pull-down, 157
code, 72 onBind method, 148
creation of, 71 onClickListener, 164
onCreate method, 148
onDestroy method, 149
S
onStartCommand method, 148
saving files to SD cards, 116 setForegroundState method, 155–156
screen layout, creating for activities, 27–29 shutting down, 149
screen sizes, handling, 75, 89 as singletons, 148
screens, defining in layout-land folder, 177–178 Start and Stop buttons, 152
SD card, saving files to, 116 startForeground method, 149
SDK (software development kit) starting, 152–154
downloading, xiv, 4 stopSelf method, 149
installing for Linux users, 6 setContentView method, 28, 33, 55
installing for Mac users, 5–6 setForegroundState method
installing for Windows users, 6 music playback, 203
SDK methods, accessing with reflection, 183–184 using, 155–156
SDK value, setting, 242 SharedPreferences, apply method, 182
SDK version number signed build
declaring support for, 181 exporting, 243–244
finding, 184 keystore file, 244
Service class sound effects, playing, 196–197
described, 148 Start and Stop buttons, adding to services, 152
onBind method, 151 StrictMode.enableDefaults, 120
ServiceExampleActivity, 152–153
services. See also communication
binding and communicating with, 164–165
T
bringing into foreground, 155 tabs, adding to action bars, 235
colon (:) in process, 161 text view
ContentObserver, 158 customizing, 65–66
Context.stopService, 149 grabbing instance of, 59–60
creating, 161–162 TextView class, 142–143
creating notifications, 155–156 TextView ID, creating for activities, 27
cursor for ContentProvider, 159 thread violations, spotting, 120. See also main thread
declaring, 113–114, 151 Toast API, 42
getting, 151 troubleshooting emulator, 18–19
ImageIntentService, 114 Twitter data, creating for list views, 136–138
IMusicService.Stub class, 164 Twitter feed
keeping running, 149 displaying, 143
lifecycle, 148 downloading, 143
main thread, 149 parsing, 143
Notification object, 156 TwitterAsyncTask, 136–138
INDEX 255
U VideoView
adding for movies, 188–189
UI (user interface)
extending OnCompletionListener, 189
AbsoluteLayout, 78–82
implementing onCompletion method, 189
altering at runtime, 58–60
setting up for, 189–190
changing visibility of views, 61–65
view clicks, tracking, 62
creating views, 54–58
ViewGroup
customizing views, 65
dip value, 78
drawable folders, 76
dp value, 78
finding resources, 59
match_parent value, 78
identifying views, 58–59
px value, 78
layout folders, 74–76
using with layouts, 77–78
LinearLayout, 82–90
wrap_content value, 78
RelativeLayout, 90–96
views. See also custom views; extended views
resource folder, 71–73
assigning IDs, 58–59
values folder, 73–74
bringing in from XML files, 176
View class, 54
centering between objects, 95
ViewGroup, 77–78
changing visibility of, 61
USB debugging, enabling, 12
click listeners, 62
usernames, saving to preferences, 182
creating, 54–58
<uses> tag, 180
findViewByID, 58–60
uses-sdkfield, including, 242
GONE visibility setting, 61
identifying, 58–59
V INVISIBLE visibility setting, 61
keeping, 60
values folders
laying out, 75
arrays, 73
LinearLayouts, 130
colors, 74
OnClickListener, 62–65
contents of, 71
onCreate method, 63
creating, 74
retrieving, 59–60
dimensions, 74
setVisibility, 61
strings, 73
VISIBLE visibility setting, 61
styles, 74
wrapping in <merge> tag, 176
version, selecting for projects, 15
in XML, 54–55
versioning
views in Java, 56–58
versionCode field, 241
dip value, 57
versionName field, 241
dp value, 57
video player, creating, 188–190
256 INDEX
fill_parent value, 57 X
match_parent value, 57
XML (Extensible Markup Language)
px value, 57
setContentView method, 55
wrap_content value, 57
views in, 54–55
XML files
W bringing in views from, 176
websites packed binary format, 73
ActionBar documentation, 236 referencing resources in, 73
Android Developers, 4 XML versus Java layouts, 60
Eclipse IDE, 4
Z
Zipalign tool, accessing and using, 245
SIGN UP TODAY
peachpit.com/creativeedge
creative
edge
Join the
PeachPit
AffiliAte teAm!