Android Programming: Pushing the Limits (2014)
Part I. Building a Better Foundation
Chapter 2. Efficient Java Code for Android
Today there are three versions of the Java platform, Java ME (Micro Edition, for certain mobile phones), Java SE
(Standard Edition, for desktops), and Java EE (Enterprise Edition, for server-side applications). When talking about Java
in general, I am usually referring to Java SE because this is the version that contains a virtual machine and a compiler.
Java code is compiled to an intermediate format called byte-code. This byte-code is then parsed by a virtual
machine on the target computer that can quickly translate it to the native format required for that particular
hardware and operating system.
Besides providing a “Write Once, Run Anywhere” advantage for developers, Java has automatic memory
management through a garbage collector (GC), reducing the need for you, as a developer, to de-allocate
memory from unused objects in your code. Although this feature is very useful and greatly reduces the risk of
introducing memory bugs in your code, because the garbage collection process needs to execute continuously,
it adds an overhead when running.
I start this chapter by doing a high-level comparison of the differences between Java SE and the Java used for
Android development. I focus on the Java SE language construct that you may be used to and how it works on
Android. Then I focus on how to optimize Java code for Android, how to optimize memory allocations, and how
to handle multithreading properly.
Comparing Android’s Dalvik Java to Java SE
Although developers were able to write applications for mobile devices using the Java programming language
long before Android, it was a severely limited version of Java called Java ME (Micro Edition). Java ME also
differed among different device manufacturers, making it almost impossible to write an application that would
work on any phone supporting Java ME. Also, the distribution of apps was very complicated because no well-
supported online stores existed at that time.
The launch of Android gave developers the option to build very capable applications for smartphones by
writing code using the Java programming language and using the same API they were used to in the standard
Java domain. However, although Android developers still use the compiler from Java SE to compile their
applications, you can find many differences between the Java that James Gosling developed and the way Java
works on your Android device.
The VM (virtual machine) that runs on an Android device is called Dalvik. It was originally developed by Dan
Bornstein at Google and provided a virtual machine suitable for CPU and memory-constrained devices. There
are several differences between Java SE and Dalvik Java, mostly regarding the virtual machine. Although Java
SE uses a stack machine design, Dalvik was designed as a register-based machine. The Android SDK has a tool
called dx that converts the Java SE stack machine byte-code to Dalvik register-based machine byte-code. The
conversion step is done automatically by your IDE so that you don’t need to bother about it.
The exact definition and technical difference between a stack-based machine and a register-based machine is
beyond the scope of this book. Android uses a register-based machine for historical reasons. Although a register-
based machine can be up to 32% faster than a stack-based machine, this is true only for virtual machines that are
interpreting the byte-code at execution (that is, interpreted virtual machines). Up until Android version 2.2 (also
known as Froyo), the Dalvik VM was purely interpreted. With the Froyo version of Android came the introduction of
a JIT compiler (Just In Time), something that Java SE had benefitted from for a long time.
JIT compilation, also known as dynamic translation, takes the byte-code and translates it into native code prior
to execution (see Figure 2-1), which has two major benefits. First, it eliminates much of the overhead associated
with pure interpreted VMs; second, it can perform optimizations to the native code that wouldn’t be possible
with statically compiled code. For instance, the JIT compiler may choose the most appropriate optimizations for
the CPU it is currently running. It’s also possible for a JIT compiler to analyze how the code is running to perform
further optimizations based on the application’s input.
Figure 2-1 Translation steps for Android Java and Java SE
As promising as this sounds, Android’s Dalvik JIT compiler has a long journey ahead before it reaches the same
level of maturity as the JIT compiler in Java SE. Still, the presence of a JIT in Dalvik provides great performance
benefits for Android, and it’s continuously being improved.
Another difference between the Java SE VM and the Dalvik VM is that the latter is optimized for running in
multiple instances on the same machine. This is handled by a process started at boot called zygote that creates
the first Dalvik instance that will be the parent of all other instances. Once an application starts, the zygote
process receives a request for a new VM instance and forks a new process that is assigned to the newly started
application, as shown in Figure 2-2. This design may seem impractical if you’re used to working with Java SE,
but it has a major advantage because it protects you from multiple application crashes in cases where one
application has a runtime failure that will crash the Dalvik VM.
Figure 2-2 Launching of new Dalvik VM instances in Android
In addition to running with a different virtual machine than the one Java SE uses, Android has a different
implementation of the APIs. All the APIs in Android that belong to the java or javax packages come from
Apache Harmony, an open-source project aiming for reimplementation of the Java SE stack. (The Apache Harmony
project has been retired since November 2011 and is no longer being actively developed.) In terms of developing,
these APIs are identical to the ones that are found in Java SE, but some differences do exist. (For instance, Google
has made major upgrades to the HttpUrlConnection class that is not present in the Java SE version.)
Also, not all of the Java SE APIs are available on Android because they’re irrelevant for this platform. For instance, the
Swing/AWT packages have been completely removed because Android uses a different UI framework. Other APIs
that have been removed are RMI, CORBA, ImageIO, and JMX. These have either been replaced by an Android specific
version (in the android package space) or simply don’t have an equivalent in Android for practical reasons.
Optimizing Java Code for Android
Java SE has evolved over the years with new features that simplify writing complicated code structures. Many
of the features make it easier for developers, and you need to understand when and how to use them properly.
Also, because Java SE has been used mostly for server-side development (using the Java Enterprise Edition
APIs), Java code has been optimized to meet server-side requirements. Annotations and support for scripting
languages in the Java SE virtual machine are examples of optimizations focused on server-side development.
Although powerful tools when building your back end, these kinds of features serve little purpose and can even
be fatal when writing client-side code as done in an Android application. Java developers have gotten used to
unlimited amounts of RAM and CPU, whereas Android development requires close attention to performance
and allocations. Simply put, you need a slightly different approach when writing code for Android as compared
to developing back end solutions in Java.
However, some recommendations have changed since Android was first released. Some modern Java
constructs once avoided on Android are now recommended, mostly because of the modern JIT compiler for
Android that removes many of the performance bottlenecks these constructs used to cause.
This section deals with the aspects of Java code that you need to understand when writing Android
applications. While the details of the Java programming language are outside the scope of this book; instead,
I focus on what is important for Android development. Still, it’s important to understand that most of the rules
and recommendations that apply to Java SE apply to Android and the Dalvik VM.
Type-Safe Enum on Android
With Java SE 5.0 came many new features in the Java programming language that made life easier for Java
developers. One of the most anticipated features was the introduction of type-safe enumerations. Enumerations
are used to represent a number of choices in code that belong to a common group. In earlier versions of Java,
multiple integer constants were used to solve this. Although this works technically, it suffers from being quite
error-prone. Take a look at the following code:
public class Machine {
public static final int STOPPED = 10;
public static final int INITIALIZING = 20;
public static final int STARTING = 30;
public static final int RUNNING = 40;
public static final int STOPPING = 50;
public static final int CRASHED = 60;
private int mState;
public Machine() {
mState = STOPPED;
}
public int getState() {
return mState;
}
public void setState(int state) {
mState = state;
}
}
The problem is that although the constants are the values you expect in the setter, nothing is preventing the
method setState() from receiving a different value. If you add a check in the setter, you need to handle the
error in case you get an unexpected value. What you want is a compile-time check that prevents you from ever
assigning an illegal value. Type-safe Java enum solves this problem, as shown here:
public class Machine {
public enum State {
STOPPED, INITIALIZING, STARTING, RUNNING, STOPPING, CRASHED
}
private State mState;
public Machine() {
mState = State.STOPPED;
}
public State getState() {
return mState;
}
public void setState(State state) {
mState = state;
}
}
Notice the new inner enum class added where you declare different states as type-safe values. This solves the
problem with unexpected values at compile time, so the code will be much less error-prone.
Before the Dalvik VM had a JIT compiler that optimized the code, using type-safe enum was discouraged on
Android because the memory and performance penalties associated with this design were greater than when
using integer constants. This is why so many integer constants are in the older parts of the Android APIs. These
days, with a capable JIT compiler and an ever-improving Dalvik VM, you don’t have to worry about that issue
and are encouraged to use type-safe enum in your application code.
However, there are still situations where integer constants are preferable. With basic types like the Java int, you
won’t increase the amount of work for the GC. Also, many existing APIs in the Android SDK still rely on basic
types as parameters—for example, the Handler class described in the “Multithreading on Android” section
later in this chapter—in these cases, you don’t have much choice.
Enhanced For-Loop on Android
Java SE 5.0 also introduced the enhanced for-loop that provides a generic an abbreviated expression for looping
over collections and arrays. First, compare the following five methods:
void loopOne(String[] names) {
int size = names.length;
for (int i = 0; i < size; i++) {
printName(names[i]);
}
}
void loopTwo(String[] names) {
for (String name : names) {
printName(name);
}
}
void loopThree(Collection<String> names) {
for (String name : names) {
printName(name);
}
}
void loopFour(Collection<String> names) {
Iterator<String> iterator = names.iterator();
while (iterator.hasNext()) {
printName(iterator.next());
}
}
// Avoid using enhanced for-loops for ArrayList
void loopFive(ArrayList<String> names) {
int size = names.size();
for (int i = 0; i < size; i++) {
printName(names.get(i));
}
}
These methods show four different ways of looping through collections and arrays. The first two methods have
the same performance, so it’s safe to use the enhanced for-loop on arrays if you’re just going to read the entries.
For Collection objects, you get the same performance when using the enhanced for-loop as when you
manually retrieve an Iterator for traversal. The only time you should do a manual for-loop is when you have
an ArrayList object.
In the cases where you not only need the value of each entry but also the position, be sure to use either an array
or an ArrayList because all other Collection classes are much slower in these situations.
In general, if you need high-quality performance when reading sets of data that rarely change, use a regular
array. However, arrays have a fixed size and will affect performance when adding data, so consider all factors
when writing your code.
Queues, Synchronization, and Locks
Frequently, an application will produce data in one thread and consume them in another. A common example
is when you’re reading data from the network on one thread and want to display the data to the user on a
different thread (the main thread where UI operations occur). This pattern is usually called the consumer/
producer pattern, and in their object-oriented programming courses, programmers may spend several hours
implementing this in their algorithm. In this section, I’ll show some of the ready-made classes that make it easier
to implement this behavior.
Smarter Queues
Many Java developers still choose to implement queues in their code using LinkedList and
synchronized blocks, although there are ready-made classes that do so and require much less code. You
can find the classes for performing concurrent programming in the java.util.concurrent package. In
addition, you can find classes for semaphores, locks, and atomic operations on single variables. Consider the
following code where a thread-safe queue using a standard LinkedList is implemented:
public class ThreadSafeQueue {
private LinkedList<String> mList = new LinkedList<String>();
private final Object mLock = new Object();
public void offer(String value) {
synchronized (mLock) {
mList.offer(value);
mLock.notifyAll();
}
}
public synchronized String poll() {
synchronized (mLock) {
while(mList.isEmpty()) {
try {
mLock.wait();
} catch (InterruptedException e) {
// Ignore for brevity
}
}
return mList.poll();
}
}
}
Although this code is correct and would probably merit a full score on an exam, it’s simply a waste of your time
to implement and test it. Instead, you can replace all the preceding code with the following line:
LinkedBlockingQueue<String> blockingQueue =
new LinkedBlockingQueue<String>();
This one line gives you the same type of blocking queue as the previous example and even provides additional
thread-safe operations. The java.util.concurrent package has a number of alternative queues and
deuce classes as well as concurrent map classes, so in general, I suggest using them rather than using lengthier
code similar to my earlier example.
Smarter Locks
The synchronized keyword in Java provides a powerful feature that allows you to make a method
or call-block thread safe. Although easy to use, it is also easy to apply it too widely, which can have very
negative impact on performance. When you need to differentiate between reading and writing data, the
synchronized keyword is not the most efficient. Luckily, a utility class in the java.util.concurrent.
locks package provides exactly that support.
public class ReadWriteLockDemo {
private final ReentrantReadWriteLock mLock;
private String mName;
private int mAge;
private String mAddress;
public ReadWriteLockDemo() {
mLock = new ReentrantReadWriteLock();
}
public void setPersonData(String name, int age, String address) {
ReentrantReadWriteLock.WriteLock writeLock = mLock.writeLock();
try {
writeLock.lock();
mName = name;
mAge = age;
mAddress = address;
} finally {
writeLock.unlock();
}
}
public String getName() {
ReentrantReadWriteLock.ReadLock readLock = mLock.readLock();
try {
readLock.lock();
return mName;
} finally {
readLock.unlock();
}
}
// Repeated for mAge and mAddress…
}
The preceding code exemplifies where to use a ReentrantReadWriteLock that allows read-only access
from multiple concurrent threads while making sure that only one thread at a time can write to the same data.
Using synchronized in your code is still a valid way of handling locks, but always consider whether a
ReentrantReadWriteLock could be a more efficient solution.
Memory Management and Allocations
The automatic memory management in Java has effectively eliminated many of the most common bugs in
software development. When you no longer have to remember to release every allocation of a new object in
your code, you save time that you can use to improve the features and overall quality of software.
But this feature doesn’t come for free because you now have an automatic garbage collector that runs in
parallel with your application. The GC will run continuously and check whether any memory allocations can be
reclaimed. This behavior means that the threads in your application will compete for CPU time with the GC, so
it’s crucial to make sure that GC calls don’t take too long whenever it is running.
Also, automatic memory management does not remove the possibility for memory leaks. If you keep references
to objects that are no longer needed, the GC will not collect them, and they will waste memory. If objects
are being allocated continuously but are never being released, you’ll eventually run into an OutOfMemory
exception, and your application will crash. So, try to avoid keeping references to objects in the main Android
components; otherwise, they may never be “garbage collected” during the application’s lifetime.
Reducing Object Allocations
On Java and Android, the most common problem with automatic memory management is when you are
allocating unnecessary objects that keep the GC working more than it should. Consider a case in which a simple
class represents a pair of integers:
public final class Pair {
public int firstValue;
public int secondValue;
public Pair(int firstValue, int secondValue) {
this.firstValue = firstValue;
this.secondValue = secondValue;
}
}
Now, say that you receive an array of integers in your application that you divide into pairs and then send to the
method sendPair. Here is an example of when memory allocation is done badly:
public void badObjectAllocationExample(int[] pairs) {
if(pairs.length % 2 != 0) {
throw new IllegalArgumentException(“Bad array size!”);
}
for(int i = 0; i < pairs.length; i+=2) {
Pair pair = new Pair(pairs[i], pairs[i+1]);
sendPair(pair);
}
}
Although this is a very crude example of how to generate Pair objects (and potentially cause a crash if the
length of the array is not an even size), it demonstrates a surprisingly common mistake: allocating objects
inside a loop. The GC will have a lot of work to do inside the previous loop above and will most likely cause the
application UI to stutter from CPU exhaustion. If you know that the sendPair method won’t keep a reference
to the Pair object after it returns, your solution is simply to move the creation of the Pair object outside the
loop and reuse the object, as shown as follows:
public void betterObjectAllocationExample(int[] pairs) {
if(pairs.length % 2 != 0) {
throw new IllegalArgumentException (“Bad array size!”);
}
Point thePair = new Point(0,0);
for (int i = 0; i < pairs.length; i+=2) {
thePair.set(pairs [i], pairs [i+1]);
sendPair(thePair);
}
}
In this new version of the method, you ensure that the object is reused during the entire run of the method.
There will be only one GC call once the method returns. Remember, when allocating objects, avoid doing so
inside a loop when possible.
Sometimes, however, you can’t avoid creating objects inside a loop, so you also need a way of handling those
situations. The solution here is to allocate objects on demand using a static factory method. (Joshua Bloch
describes this method in detail in Item 1 of his book Effective Java. )
This approach is commonly used in the Android framework and APIs and allows you to use a behind-the-scenes
object cache that is populated on demand. The only drawback is that you need to manually recycle the objects,
or the cache will always be empty.
Based on your previous example, you start by refactoring the Pair class so that you have a simple pool for
reusing objects.
public final class Pair {
public int firstValue;
public int secondValue;
// Reference to next object in the pool
private Pair next;
// The lock used for synchronization
private static final Object sPoolSync = new Object();
// The first available object in the pool
private static Pair sPool;
private static int sPoolSize = 0;
private static final int MAX_POOL_SIZE = 50;
/**
* Only allow new objects from obtain()
*/
private Pair() { }
/**
* Return recycled object or new if pool is empty
*/
public static Pair obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Pair m = sPool;
sPool = m.next;
m.next = null;
sPoolSize--;
return m;
}
}
return new Pair();
}
/**
* Recycle this object. You must release all references to
* this instance after calling this method.
*/
public void recycle() {
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
}
Note that a number of fields are added, both static and non-static. You use these fields to implement a
traditional linked list of Pair objects. Objects of this class are created only through the obtain method.
You prevent new objects from outside this class by making the constructor private. The obtain method first
checks whether the pool contains any existing objects and removes the first element in the list and returns it. If
the pool is empty, it simply creates a new object. To put the object back into the pool, you call recycle once
you’re done. At that point, it is very important that you don’t touch that object again.
With this modification of the Pair class, your previous method with the loop also needs some changes.
public void bestObjectAllocationExample(int[] pairs) {
if(pairs.length % 2 != 0) throw new IllegalArgumentException (“Bad
array size!”);
for (int i = 0; i < pairs.length; i+=2) {
Pair pair = Pair.obtain();
pair.firstValue = pairs[i];
pair.secondValue = pairs[i+1];
sendPair(pair);
pair.recycle();
}
}
The first time this method runs, you create a new instance of Pair, which will then be reused for every new
iteration. However, the next time you run this method, no new object is created. Also, because the obtain
and recycle methods are thread-safe, this method is safe to execute in multiple concurrent threads. The only
drawback is that you have to remember to call recycle manually, but this is a small price to pay considering
you’ve postponed all GC calls for the Pair class until the application exits.
The example with the Pair class is very trivial, but it illustrates a pattern that is highly encouraging if you have
a class that would otherwise be created frequently in your code. This design may look familiar because it’s in
several places in the Android source code and APIs. Commonly used classes such as Message, MotionEvent,
and Parcel all implement this pattern in order to reduce unnecessary GC calls. The example with the
preceding Pair class is basically a copy of the Message class implementation. When using this approach,
remember to call recycle once you’re done with an object that implements this, or the pool will be empty all
the time.
Multithreading on Android
One of the most difficult parts of programming is when you have to write code that executes on multiple
threads. It’s a requirement for modern applications because you can’t have everything executing in a serial
order on one single thread. An Android application starts with a thread called main, also known as the UI thread
(in this book, I use main and UI thread interchangeably). Unless you start another thread or implicitly call a
function that starts a new thread, everything you do in an Android application will execute on the main thread.
This means that if your code performs an operation on the main thread (say, running code in the onResume
method) that will take a significant amount of time, all drawing and input events will be blocked until that
operation is completed. So, the first thing to remember when you start writing your application code: Make sure
that any code you execute won’t ever block the main thread.
But how do you know if a method is executing on the main thread? The official Android documentation
states: “By default, all components of the same application run in the same process and thread (called the ‘main’
thread)." More specifically, all the callbacks (basically, all the onX methods) in the Android components
(Activity, BroadcastReceiver, Service, and Application) are executed on the same thread. The
onStartCommand method of a Service is thus running on the same thread as the onResume of your
Activity. Remember this when you design your code because blocking one of these methods will cause the
system to “kill” your application.
The main thread in Android will run as long as your application’s process is alive. It is kept alive during the
application life cycle by the Looper class. This class creates a loop inside the current thread that queries a
message queue (using the MessageQueue class). The query to this queue will block until a new message has
arrived, which ensures that the thread sleeps when there’s nothing to do. All execution on the main thread is
done by sending messages to this queue, either directly through a Handler object or indirectly through some
part of the Android API (for instance, the runOnUiThread method). You can retrieve the Looper for the main
thread of your application through Context.getMainLooper().
What is safe to execute on the main thread and what should be moved to a different thread? To be very strict,
only the methods that must be executed on the main thread should be executed there. Everything else should
go on a separate thread. For practical reasons, you can bend this rule in some cases where the operation
you perform will never take a long time. If you make sure that any file, database, or network operations are
performed on a separate thread, you are usually safe. Also, for certain applications and games, you will perform
calculations at regular intervals that don’t directly have anything to do with the UI, and these should also
execute on a separate thread. However, it’s also important to ensure that you don’t have too many threads
running at the same time because a performance penalty is involved when the CPU switches from one thread
to another. In later chapters in this book, I give more specifics on when and when not to put your code on a
separate thread.
How do you declare and manage your threads when writing Android code? In the following section I show you
several ways to spawn new threads, and I explain each of these methods in detail, including their pros and cons.
Thread
This is the base class for all threads in Android. It is the same class as found in Java SE, and it behaves the same
way. If you want to execute something inside a thread, you can either make a specialization (that is, a new class
that extends Thread) or create a class implementing the Runnable interface that you pass to the constructor
of Thread. This example uses the latter option.
In this example, you need to iterate over an array of Objects in order to “upload” data to a server (the actual
code for uploading is not part of this example). You need to execute this operation on a separate thread;
otherwise, it will block the user interface. Also, the operation needs to update its progress by increasing a
ProgressBar. The following code shows an implementation of Runnable that resolves this issue:
public class MyThread implements Runnable {
private Object[] mInput;
private Activity mActivity;
private int mProgress = 0;
public MyThread(Activity activity, Object ... input) {
mActivity = activity;
mInput = input;
}
@Override
public void run() {
mProgress = 0;
Runnable runnable = new Runnable() {
public void run() {
mActivity.findViewById(R.id.progressBar).
setVisibility(View.VISIBLE);
((ProgressBar) mActivity.
findViewById(R.id.progressBar)).setProgress(0);
}
};
mActivity.runOnUiThread(runnable);
// Loop through and process mInput
for (Object input : mInput) {
// Post the input to a server (fake it with a sleep)
SystemClock.sleep(50);
runnable = new Runnable() {
public void run() {
((ProgressBar) mActivity.
findViewById(R.id.progressBar)).
setMax(++mProgress);
((ProgressBar) mActivity.
findViewById(R.id.progressBar)).
setProgress(mInput.length);
}
};
mActivity.runOnUiThread(runnable);
}
runnable = new Runnable() {
public void run() {
mActivity.findViewById(R.id.progressBar).
setVisibility(View.INVISIBLE);
}
)
};
mActivity.runOnUiThread(runnable);
}
}
As you can see in the preceding example, you need to create a new Runnable every time you want to update
the UI. This makes the code messy and will also cause unnecessary object allocations that need to be garbage
collected, which is always a bad thing. The Runnable is needed in order to post UI updates back to the main
thread through the runOnUiThread method.
There’s another problem with this solution: Because you can call start on an instance of Thread only once,
you must create a new Thread object every time you need to perform this operation. A new thread is an
expensive thing to create again and again, so there is definitively room for improvement here. All together, this
is not a very flexible method, and I discourage the direct use of the Thread class.
AsyncTask
This class is probably one of the more popular classes in Android because it is so simple to use. It allows you
to define a task that will run on its own thread and provide callbacks for the different stages of the task. The
callbacks are also designed to remove the need for using the runOnUiThread method to update the UI while
it’s running, which makes it very appropriate for indicating the progress of a long-running operation. Here is an
AsyncTask that does the same thing as in the Thread example:
public class MyAsyncTask extends AsyncTask<String, Integer, Integer> {
private Activity mActivity;
public MyAsyncTask(Activity activity) {
mActivity = activity;
}
@Override
protected void onPreExecute() {
super.onPreExecute();
// This will run on the main thread
mActivity.findViewById(R.id.progressBar).
setVisibility(View.VISIBLE);
((ProgressBar) mActivity.findViewById(R.id.progressBar)).
setProgress(0);
}
@Override
protected Integer doInBackground(String... inputs) {
// This will run NOT run on the main thread
int progress = 0;
for (String input : inputs) {
// Post the input to a server (fake it with a sleep)
SystemClock.sleep(50);
publishProgress(++progress, inputs.length);
}
return progress;
}
@Override
protected void onProgressUpdate(Integer... values) {
// This will run on the main thread
((ProgressBar) mActivity.findViewById(R.id.progressBar)).
setMax(values[1]);
((ProgressBar) mActivity.findViewById(R.id.progressBar)).
setProgress(values[0]);
}
@Override
protected void onPostExecute(Integer i) {
super.onPostExecute(i);
// This will run on the main thread
mActivity.findViewById(R.id.progressBar).
setVisibility(View.INVISIBLE);
}
}
In the preceding example, you see the implementation of four callbacks with a comment regarding which
thread they will execute on. As you can see, onPreExecute, onProgressUpdate, and onPostExecute
are all executed on the main thread, so it is safe to update the UI from these. publishProgress is also called
for each input to trigger the onProgressUpdate callback so that you can update a progress bar.
This class makes it much easier to perform long-running operations on a different thread and still be able to
communicate easily with the main thread when needed. The only problem with AsyncTask is that you can
use each instance of this class only once, which means that you have to call new MyAsyncTask() every time
you want to perform this operation. Although this class is not a heavyweight—the actual thread is managed
by an ExecutorService behind the scenes—it is not suitable for operations that you perform frequently
because you would quickly gather up objects that need to be garbage collected and eventually cause your
application’s UI to stutter.
In addition, you cannot schedule the time for the execution of this operation or perform the operation at a
certain interval. The AsyncTask class is suitable for things like file downloads or similar situations that will
happen relatively infrequently or by user interaction. Nevertheless, because it’s so easy to implement, this is
likely to be the class you will use at first in your application.
Handler
When you need a more fine-grained control for executing operations on a separate thread, you have a great
utility at your disposal in the Handler class. This class allows you to schedule operations with exact precision,
and you can reuse it as many times as you want. The thread that executes the operations loops until you
explicitly terminate it; the class Looper takes care of this behind the scenes. You will rarely need to set up your
own Looper ; instead, you can create it through the wrapper called HandlerThread. The following example
shows how to create a new Handler in your Activity.
public class SampleActivity extends Activity implements Handler.Callback
{
private Handler mHandler;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Start a new thread with a Looper
HandlerThread handlerThread
= new HandlerThread(“BackgroundThread”);
handlerThread.start();
// Create your new Handler
mHandler = new Handler(handlerThread.getLooper(), this);
}
@Override
protected void onDestroy() {
super.onDestroy();
// Shut down the Looper thread
mHandler.getLooper().quit();
}
@Override
public boolean handleMessage(Message message) {
// Process incoming messages here...
// Recycle your message object
message.recycle();
return true;
}
}
With your new Handler object, you can now safely schedule operations with exact precision. The usual way
of working with a Handler is by sending Message objects, which are simple, cheap, and reusable objects
for passing data and arguments to your background thread. A Message object is usually defined by its public
integer field name what that works as a flag that can be used in a switch-case statement in the callback
handleMessage. There are also two integer fields named arg1 and arg2 that are suitable for low-cost
arguments, as well as a field name obj for storing a single arbitrary object reference. If needed, you can also
add a set of more complex data using the setData method with a standard Bundle object. You can send your
messages to the handler using a number of methods. The three most common ones are demonstrated here:
public void sendMessageDemo(Object data) {
// Create a new Message with data as a parameter
// and send it for execution on the handler immediately
Message.obtain(mHandler, SYNC_DATA, data).sendToTarget();
// Send a simple empty message on your handler immediately
mHandler.sendEmptyMessage(SYNC_DATA);
// Send a simple empty message to be executed 30 seconds from now
mHandler.sendEmptyMessageAtTime(SYNC_DATA,
THIRTY_SECONDS_IN_MILLISECONDS);
// Send a message with both arguments fields and
// the obj set to be executed in two minutes.
int recipient = getRecipientId();
int priority = 5;
Message msg = mHandler.obtainMessage(SYNC_DATA, recipient,
priority, data);
mHandler.sendMessageDelayed(msg, TWO_MINUTES_IN_MILLISECONDS);
}
The first two examples show that you can create and send messages through both the Message class and the
Handler object. In the third and fourth example, you see how to schedule the message for processing with
millisecond precision.
When the Message is processed from the message queue, it will be sent, on the looping thread, to the callback
you’ve implemented. You can use the same callback for multiple Handler objects, which makes it useful as a
proxy-like method for dealing with the messages in your application. You can even share the callback between
Activities and Services. The most optimal way of implementing your callback is by keeping all the
constants representing the what value of a Message in the same class that implements the callback and then
use a standard switch-case statement to handle each message type. In the preceding example, I implemented
the callback on the Activity, but it can often be useful to have a separate class for this that you pass your
application’s Context to so that it can be used in all parts of your application. Here is an example of a typical
callback:
// Constants used for the what field in a Message
public static final int SYNC_DATA = 10;
public static final int PING_SERVER = 20;
@Override
public boolean handleMessage(Message message) {
switch (message.what) {
case SYNC_DATA:
// Perform long-running network I/O operation
syncDataWithServer(message.obj);
break;
case PING_SERVER:
// Ping your server. This should be called at regular intervals
pingServer();
break;
}
// Recycle your message object to save memory
message.recycle();
return true;
}
The handleMessage callback in this example has only two operations implemented, SYNC_DATA and
PING_SERVER. The first one will probably be triggered by a user event—for example, when a file is saved or
new data is ready to be posted to a server. The second one is supposed to execute at regular intervals. However,
there is no method on the Handler class for sending messages at an interval, so you need to implement that
behavior.
Scheduling Operations at Recurring Intervals
Say that as soon as your Activity starts, you want to start pinging the server every minute. When the user
exits the Activity, you should stop pinging.
In the following example, I add calls to the Handler in onResume() and onPause() (see the earlier
examples in this section for the setup of the Handler instance), effectively making these happen when the
Activity is shown and dismissed. In onResume, I set the boolean for pinging the server to true and sent
a PING_SERVER message to be executed immediately (the first ping should happen as soon as possible). The
message arrives in your callback message shown in the previous example, which calls your pingServer()
method.
public class SampleActivity extends Activity implements Handler.Callback {
private static final String PING_URL = “http://www.server.com/ping”;
private static final int SIXTY_SECONDS_IN_MILLISECONDS = 60 * 1000;
public static final int SYNC_DATA = 10;
public static final int PING_SERVER = 20;
private Handler mHandler;
private boolean mPingServer = false;
private int mFailedPings = 0;
... code from previous examples omitted for brevity
@Override
protected void onResume() {
super.onResume();
mPingServer = true;
mHandler.sendEmptyMessage(PING_SERVER);
}
@Override
protected void onPause() {
super.onPause();
mPingServer = false;
mHandler.removeMessages(PING_SERVER);
}
private void pingServer() {
HttpURLConnection urlConnection
try {
URL pingUrl = new URL(PING_URL);
urlConnection = (HttpURLConnection) pingUrl.openConnection();
urlConnection.setRequestMethod(“GET”);
urlConnection.connect();
if(urlConnection.getResponseCode() == 200) {
mFailedPings = 0;
} // Here you should also handle network failures...
} catch (IOException e) {
// Network error should be handled here as well...
} finally {
if(urlConnection != null) urlConnection.disconnect();
}
if(mPingServer) {
mHandler.sendEmptyMessageDelayed(PING_SERVER,
SIXTY_SECONDS_IN_MILLISECONDS);
}
}
}
In pingServer(), you make a simple HTTP call to see if your server is alive. Once your call is completed, you
check whether you should keep pinging the server, and if so, you schedule a new message to be processed in
60 seconds. In the onPause() method, you change the boolean to false and then remove any pending
messages with the what field set to PING_SERVER.
Using the MainLooper with a Handler
Because you assign a thread to a Handler by giving it a Looper object in the constructor, you can create a
Handler that processes messages on the main thread. Doing so is especially useful when you want to avoid
using the runOnUiThread() method, which tends to result in rather ugly and less optimal code if used often.
I use this pattern frequently in my applications so that I can simply send messages between the main thread
and the thread used for background operations.
@Override
public boolean handleMessage(Message message) {
switch (message.what) {
case SYNC_DATA:
syncDataWithServer(message.obj);
break;
case SET_PROGRESS:
ProgressBar progressBar =
(ProgressBar) findViewById(R.id.progressBar);
progressBar.setProgress(message.arg1);
progressBar.setMax(message.arg2);
break;
}
message.recycle();
return true;
}
In the preceding handleMessage example, you can receive two types of messages, SYNC_DATA and SET_
PROGRESS. The first one needs to run on a separate thread, whereas the second one must run on the main
thread because it will update the UI. To do this, you create an additional Handler object that will be used to
send messages that are processed on the main thread.
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mMainHandler = new Handler(getMainLooper(), this);
HandlerThread handlerThread = new HandlerThread(“BackgroundThread”);
handlerThread.start();
mHandler = new Handler(handlerThread.getLooper(), this);
}
Notice that this is basically the same onCreate method as shown earlier. The exception is the line where you
create mMainHandler. Instead of starting a new HandlerThread, you simply retrieve the Looper for
the main thread. This doesn’t affect how the main thread is running; you just connect an additional Handler
and callback to the main thread. Messages posted to this Handler are processed by the same callback as the
second Handler you create for background operations. When you want to update the ProgressBar, you
send a simple message, as shown here:
Message.obtain(mMainHandler, SET_PROGRESS, progress, maxValue).
sendToTarget();
You can use this approach for any operation that must be executed on the main thread. You can either send
simple Message objects as just shown or pass along more complex data in the obj field or as a Bundle in
setData. You just need to make sure that you post messages to the correct Handler.
Picking the Right Solution for Threads
I’ve now shown three different methods for creating and using threads on Android. There are other utilities in
the API for doing this as well, such as the ExecutorService and the Loader. The ExecutorService is
useful if you need to have multiple operations running in parallel, which is usually the situation when writing
server-side applications responding to multiple clients. This Service is also used behind the scenes in
AsyncTask, and if you want to be able to execute multiple AsyncTasks in parallel, you can do so using the
right ExecutorService.
The three preceding examples are the ones to start with when you need a dedicated thread for an operation.
Using the Thread class directly is discouraged unless you need full control of the entire execution of the
thread. AsyncTask and Handler are recommended in most cases, and which one you choose depends
on your needs. If the operation isn’t performed frequently, like more than once every minute, AsyncTask is
probably a good choice. If you need to schedule operations or need to do something at a fast, recurring interval,
Handler is a better choice. Working with the Handler tends to generate less code in the long run, whereas
AsyncTask is easier to use.
Summary
In this chapter, I covered some of the advanced Java features in Java SE 5.0 that with the modern JIT-enabled
Dalvik VM are now safe to use on Android. Understanding and using these features will likely simplify your code,
which makes the code easier to test and maintain. I also showed how to use the concurrency API from java.
util.concurrent, instead of implementing your own queues or locks. Whenever you reinvent the wheel
by implementing your own version of a basic class, you’re making a common but big mistake: You’ll have more
code to test, more code that can cause bugs, and more code to maintain.
I also explained how to avoid many pitfalls when it comes to memory allocations. If your code creates a lot
of temporary and short-lived objects, your application will most likely perform badly in the user interface.
Understanding how to efficiently and safely reuse objects allows for a much smoother user experience.
To conclude the chapter, I covered three methods for using threads on Android but recommended using only
two of them (AsyncTask and Handler). Multithreading is a complex topic and often the cause of many bugs
that are hard to find. Always try to use the ready-made utility classes for threads because they will make things
simpler and allow you to focus on the functions of your own code.
Java is a powerful language that makes it easier for developers to express what they want to achieve and
learning how to use the language most efficiently will make you a better developer and help you create high-
quality code.
Further Resources Documentation
I recommend reading the performance tips chapter at: http://developer.android.com/training/best-performance.html
Books
Bloch, Joshua. Effective Java, 2nd Edition . Addison-Wesley, 2008.
Online Sources
Google IO video sessions on YouTube at: www.youtube.com/user/GoogleDevelopers