Writing Seamless Android Applications
Don't Drop Data
Always keep in mind that Android is a phone platform. It may seem obvious to say it, but it's
important to remember that another Activity (such as the "Incoming Phone Call" app) can pop
up over your own Activity at any moment. This will fire the onFreeze() and onPause() methods,
and will likely result in your application being killed.
If the user was editing data in your application when the other Activity appeared, your application
will likely lose that data when your application is killed. Unless, of course, you save the work in
progress first. The "Android Way" is to do just that: Android applications that accept or edit input
should override the onFreeze() method and save their state in some appropriate fashion. When
the user revisits the application, she should be able to retrieve her data.
A classic example of a good use of this behavior is a mail application. If the user was composing
an email when another Activity started up, the application should save the in-process email as a
No One Wants to See Your Data Naked
If you wouldn't walk down the street in your underwear, neither should your data. While it's possible
to expose certain kinds of application to the world to read, this is usually not the best idea. Exposing
raw data requires other applications to understand your data format; if you change that format, you'll
break any other applications that aren't similarly updated.
The "Android Way" is to create a ContentProvider to expose your data to other applications via a
clean, well-thought-out, and maintainable API. Using a ContentProvider is much like inserting a Java
language interface to split up and componentize two tightly-coupled pieces of code. This means
you'll be able to modify the internal format of your data without changing the interface exposed by the
ContentProvider, and this without affecting other applications.
Don't Interrupt the User When He's Talking
If the user is running an application (such as the Phone application during a call) it's a pretty safe
bet he did it on purpose. That's why you should avoid spawning Activities except in direct response
to user input from the current Activity.
That is, don't spawn Activities from IntentReceivers or Services running in the background. Doing so
will interrupt whatever application is currently running, and result in an annoyed user. Perhaps even
worse, your Activity may become a "keystroke bandit" and receive some of the input the user was in
the middle of providing to the previous Activity. Depending on what your application does, this could
be bad news.
Instead of spawning Activities directly from the background, you should instead use the
NotificationManager to set Notifications. These will appear in the status bar, and the user can then
click on them at his leisure, to see what your application has to show him.
(Note that all this doesn't apply to cases where your own Activity is already in the foreground: in that
case, the user expects to see your next Activity in response to input.)
Got a Lot to Do? Take it to a Thread
If your application needs to perform some expensive or long-running computation, you should
probably move it to a thread. This will prevent the dreaded "Application Not Responding" dialog from
being displayed to the user, with the ultimate result being the fiery demise of your application.
By default, all code in an Activity as well as all its Views runs in the same thread. This is the same
thread that also handles UI events. For example, when the user presses a key, a key-down event is
added to the Activity's main thread's queue. The event handler system needs to dequeue and handle
that event quickly; if it doesn't, the system concludes after a few seconds that the application is hung
and offers to kill it for the user.
If you have long-running code, running it inline in your Activity will run it on the event handler thread,
effectively blocking the event handler. This will delay input processing, and result in the ANR dialogs.
To avoid this, move your computations to a thread; click here to learn how.
Avoid Huge Activities
Any application worth using will probably have several different screens. When partitioning your UI,
be sure to make effective use of Activities.
Depending on your development background, you may interpret an Activity as similar to something like
a Java Applet, in that it is the entry point for your application. However, that's not quite accurate:
where an Applet subclass is the single entry point for a Java Applet, an Activity should be thought of
as one of potentially several entry points to your application. The only difference between your "main"
Activity and any others you might have is that the "main" one just happens to be the only one that
expressed an interest in the "android.intent.action.MAIN" action in your AndroidManifest..xml file.
So, when designing your application, think of your application as a federation of Activities. This will
make your code a lot more maintainable in the long run, and as a nice side effect also plays nicely
with Android's application history and "backstack" model.
When it comes to the look-and-feel of the user interface, it's important to blend in nicely. Users are
jarred by applications which contrast with the user interface they've come to expect. When designing
your UIs, you should try and avoid rolling your own as much as possible. Instead, use a Theme.
You can override or extend those parts of the theme that you need to, but at least you're starting from
the same UI base as all the other applications. For all the details, click here.
Make Being Flexible part of your Resolutions
Different Android devices will sport different resolutions. Some will even be able to change resolutions
on the fly, such as by switching to landscape mode. It's important to make sure your layouts and
drawables are flexible.
Fortunately, this is very easy to do. Check out Implementing a User Interface for the full details, but in
brief what you must do is provide different versions of your artwork (if you use any) for the key
resolutions, and then design your layout to accommodate various dimensions. (For example, avoid
using hard-coded positions and instead use relative layouts.) If you do that much, the system handles
the rest, and your application looks great on any device.
Assume the Network is Slow
Android devices will come with a variety of network-connectivity options. All will have some
data-access provision, though some will be faster than others. The lowest common denominator,
however, is GPRS, the non-3G data service for GSM networks. Even 3G-capable devices will spend
lots of time on non-3G networks, so slow networks will remain a reality for quite a long time to come.
That's why you should always code your applications to minimize network accesses and bandwidth.
You can't assume the network is fast, so you should always plan for it to be slow. If your users
happen to be on faster networks, then that's great — their experience will only improve. You want to
avoid the inverse case though: applications that are usable some of the time, but frustratingly slow
the rest based on where the user is at any given moment are likely to be unpopular.
One potential gotcha here is that it's very easy to fall into this trap if you're using the emulator, since
the emulator uses your desktop computer's network connection. That's almost guaranteed to be
much faster than a cell network, so you'll want to change the settings on the emulator that simulate
slower network speeds. You can do this in Eclipse, in the "Emulator Settings" tab of your launch
configuration or via a command line option when starting the emulator.
Different Keystrokes for Different Folks
Android will support a variety of handset form-factors. That's a fancy way of saying that some Android
devices will have full "QWERTY" keyboards, while others will have 40-key, 12-key, or even other key
configurations. Similarly, some devices will have touch-screens, but many won't.
When building your applications, keep that in mind. Don't make assumptions about specific keyboard
layouts -- unless, of course, you're really interested in restricting your application so that it can only
be used on those devices.
Don't Assault the Battery
A wireless device isn't very wireless if it's constantly plugged into the wall. Handheld devices are
battery-powered, and the longer we can make that battery last on a charge, the happier everyone is
-- especially the user. Two of the biggest consumers of battery power are the processor, and the
radio; that's why it's important to write your applications to do as little work as possible, and use the
network as infrequently as possible.
Minimizing the amount of processor time your application uses really comes down to writing efficient
code. To minimize the power drain from using the radio, be sure to handle error conditions gracefully,
and only fetch what you need. For example, don't constantly retry a network operation if one failed.
If it failed once, it's likely because the user has no reception, so it's probably going to fail again if
you try right away; all you'll do is waste battery power.
Users are pretty smart: if your program is power-hungry, you can count on them noticing. The only
thing you can be sure of at that point is that your program won't stay installed very long.