Now let's talk about some other options, or possibly some pieces of general information that are relevant to this task.
Under the hood, an Android application is not a unique block of instructions executed one after the other, but is composed of multiple pipelines of execution. The main concepts here are the process and thread. When an application is started, the operating system creates a process (technically a Linux process) and each component is associated to this process.
Together with the process, a thread of execution named main
is also created. This is a very important thread because it is in charge of dispatching events to the appropriate user interface elements and receiving events from them. This thread is also called UI Thread.
It's important to note that the system does not create a separate thread for each element, but instead uses the same UI thread for all of them. This can be dangerous for the responsiveness of your application, since if you perform an intensive or time expensive operation, this will block the entire UI. All Android developers fight against the ANR (Application Not Responding) message that is presented when the UI is not responsive for more than 5 seconds.
Following Android's documentation, there are only two rules to follow to avoid the ANR:
These two rules can seem simple, but there are some particulars that have to be clear. First of all, let me show you the simplest way to create a new thread, using the class named Thread
.
This class implements the Runnable
interface defined with a single method called run()
; when an instance of a Thread
calls its own method start()
, it launches in the background the instructions defined in the run()
method. Nothing new for everyone with experience in Java programming; this is plain Java, so it is completely available in all API levels.
For example, if we want to create a simple task that sleeps for 5 seconds, without blocking the UI, we can use the following piece of code:
All is clear, but in a general case, we would like to interact with the UI, in order to update a progress bar, to show an error, or to change the appearance of a UI element; using an example from Android's documentation, we are tempted to write a piece of code where we update an ImageView
by using a remote PNG:
All seems ok, but when running this code, it results in an infamous exception appearing in the application's log:
Only the original thread that created a view hierarchy can touch its views.
This is because setImageBitmap()
is executed in the thread created by us and not in the UI thread, violating the second rule expressed above (this is not allowed since the UI thread is not thread-safe, that is, it is not assured that concurrent access to an element doesn't cause problems).
Before we solve this problem, let me show you the innermost structures introduced by the Android system to manage threads—the Looper
and Handler
classes.
An instance of the first class is simply used to run a message loop in a thread that will be handled by an instance of the second class. On the other hand, a Handler
instance manages message instances between threads, but its context of execution is the thread where it was initially defined.
In order to understand, it's better to write a complex example involving two threads communicating with messages. Suppose we have a generic Activity class, and inside its onCreate()
method, we define two threads communicating after every 5 seconds:
This is how it appears in Eclipse's thread panel when the code is running:
The more fascinating thing is that there is also a possibility to queue the Runnable
classes to be executed in the original thread of the Handler
class. Instead of sendMessage()
, it is possible to use mFirstHandler.post()
with a Runnable
class's definition as the argument.
The fundamental point to remember in order to use these classes is to call Looper.prepare()
and Looper.loop()
in the run()
method of the thread and the code related to the Handler
class in between—that's all.
The only thread that has yet a Looper defined is the UI Thread that makes some methods available in order to post the Runnable
class instance in it.
Now, back to the earlier problem, let me explain how to solve it using the Runnable
class; we can post the updating UI code by using a utility method available to any View
, such as the View.post(Runnable)
method.
Now, we can substitute the line causing the problem with the following code:
Looper
and Handler
are important since they are at the core of the system, and more importantly, they have been available since API level 1, making them good resources for writing the Android applications.
Another important class, available since API level 3, is AsyncTask
. If you have worked on an application using background threads, it is probable that you have used it since it is intended for this purpose; to facilitate the managing of the threads, and to avoid all the headache and the error-prone code of the Looper
and Handler
classes.
Its definition is particular. It uses generics; that is, there are some parameters indicated with Params
, Progress
, and Result
that identify the signature of some functions used internally to manage threads.
In particular, AsyncTask
has four methods as follows:
void onPreExecute()
: Its role is to set up the task.
protected Result doInBackground(Params...)
: This is the core of the AsyncTask
class and your code has to be written here. Just after onPreExecute()
is terminated, a background thread is created for the execution of this function. It's important to remember not to attempt to update the UI from this function. Use the onProgressUpdate()
to post updates back to the UI.
onProgressUpdate(Progress...)
: This is used to publish progresses in some way.
onPostExecute(Result)
: This receives the result of the doInBackground()
function.
All but the doInBackground()
function are executed in the UI thread, so it's important to remember not to perform time-consuming work in them.
If we want to replicate the code that downloads a remote PNG and updates an ImageView
with it, we should write something, as shown in the following code snippet:
For where we will want to call it, we have to insert a line, such as the following:
What you may have noted is that in the initial steps, when we defined our Loader, we subclassed a class named AsyncTaskLoader
. It is simply a Loader with an AsyncTask
inside; the only difference here is that it doesn't get three parameters in its definition, but only one since it's not supposed by a Loader to return information about the status of an operation (for example, no progress bar is shown).
A final note from the documentation about the serial/parallel execution of threads:
When first introduced, AsyncTasks were executed serially on a single background thread. Starting with DONUT, this was changed to a pool of threads allowing multiple tasks to operate in parallel. Starting with HONEYCOMB, tasks are executed on a single thread to avoid common application errors caused by parallel execution.
If you truly want parallel execution, you can invoke executeOnExecutor(java.util.concurrent.Executor, Object[]) with THREAD_POOL_EXECUTOR.
General structure of a Loader
The initial instructions about writing a Loader have used the simple AsyncTaskLoader
that simplifies a lot for the life of a developer, creating for you the correct subdivision between background threads and UI threads.
This is important, mainly since it avoids wasting your time with little errors, and more importantly, makes the code more modular, avoiding the need of reinventing the wheel. However, now we are to reinvent the wheel in order to understand how to correctly manage the Loader
classes with your applications.
The Loader is intended to be used with dynamic data, where it is important to be notified for updates in order to refresh the related element of the UI; in order to notify our loader that the underlying data is changed, we'll implement a class named RSSObservable
that controls that the XML (representing the RSS) is different from the previous version. It's important to note that this is a proof of concept and is not intended to be used in the real world. Both the Loader and the Observable classes download the RSS, causing the drain of the battery (and in some case, you will be billed for the bandwidth).
Once you read this code, try to compare it with the original implementation of the AsyncTaskLoader
class that you can find in Android's source code in the file frameworks/base/core/java/android/content/AsyncTaskLoader.java
. Obviously, we are not going to implement all the things that you can find there.
So let's implement our custom Loader:
Import the required classes:
Define our custom loader, extending the Loader
class and indicating the implementation of the Observer
interface:
Define the internal variables that will reference the Task
and Observable
instances:
Define the constructor where we initialize all the things needed for the class to work correctly.
Define a customized AsyncTask
that returns the data of your choice; in its doInBackground()
method, simply do the same as the previous example. onPostExecute()
warns LoaderManager
of the concluded task.
Now implement the behavior for the main actions that can be performed on a Loader:
Implement the deliverResult()
method:
Write the callback of the Observer
interface:
Write a class representing the Observable
interface, where we implement the code that watches and notifies us of data changes:
The more cumbersome part is understanding the underlying flow of the Loader. First of all, there are three states in which it can exist. Those are as follows:
STARTED
: Loaders execute their loads and notify the Activity class using onLoadFinished()
.
STOPPED
: Loaders continue to monitor for changes, but must not deliver results. This state is induced by calling stopLoading()
from LoaderManager
when the related Activity/Fragment class is being stopped.
RESET
: Loaders must not monitor changes, deliver results, and so on. The data already collected should be garbage collected.
Each of these states can be reached from the others.
Since all happens asynchronously, it's possible that a notification of data update can reach the Loader
instance when the state is different from STARTED
; this explains the various checks present in the code.
One thing introduced in the preceding code snippet, not mentioned in the AsyncTaskLoader
example, is the Observer/Observable design pattern. The first is defined as an interface and the second as a class, both in the java.util
package (and both have been available from API level 1, so do not cause compatibility issues). The observer receives notification of updates by the update()
method, whereas the observable registers some observers (by the addObserver()
method) to be notified (by the notifyObservers()
method) when a change occurs.
Tip
A last note
cancelLoad()
is not present in the Loader
class version of the Compatibility Library.