Front-End Web & Mobile
Making Asynchronous Calls with Handler
There are two ways to make asynchronous calls: AsyncTask
, and Handler
plus Thread
. I briefly introduced how to use AysncTask
to make asynchronous calls in the previous post. In this post, I will explain making asynchronous calls with Handler
. I will also compare AsyncTask
with Handler
.
What is Handler?
A Handler
is associated with a thread that creates the handler. It receives and processes messages and Runnable
objects that are sent from a different thread. With Handler
, you can run a time-consuming task in a background thread and send messages to the main thread to update the UI. I will use uploading an image to Amazon S3 as an example to explain how to use Handler
to make asynchronous calls.
Defining a Thread Handler
An Android application has a default thread — the main thread. When a Handler
object is created in onCreate()
, it’s bonded to the main thread. You override the handlerMessage(Message msg)
method in order to process messages that are sent to the handler. The Message
class consists of a user-defined message code what
, two integer value arguments arg1
and arg2
, and an arbitrary object obj
. Not all fields are set by the sender. Usually, at minimum the message code is set to identify an event.
In the process of uploading an image to Amazon S3
, you are likely to be interested in three events: when the upload starts, when data is transferring, and when the upload finishes. Therefore, three message codes, S3_UPLOAD_START, S3_UPLOAD_PROGRESS, and S3_UPLOAD_FINISH, are used to identify these events, respectively. Based on the message, you can use a switch
statement to perform a corresponding operation.
Handler handler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case S3_UPLOAD_START: // Set up a progress dialog when upload starts dialog = new ProgressDialog(...); dialog.setMessage(getString(R.string.uploading)); dialog.setCancelable(false); dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); dialog.setMax(msg.arg1); dialog.show(); break; case S3_UPLOAD_PROGRESS: // Update progress dialog.setProgress(msg.arg1); break; case S3_UPLOAD_FINISH: // When upload finishes, dismiss progress dialog dialog.dismiss(); break; default: break; } } }
Running a Task in a Thread
As a general rule, it’s best to not block the main thread in Android. So time-consuming tasks must be run in a background thread. You can extend the Thread
class and implement the time-consuming task inside the run()
method, which you kick off by calling start()
. In order to communicate with the main thread, messages have to be created and sent to the main thread’s handler. Handler
has a convenient method, obtainMessage(...)
, to create a message. You can define message code and include additional data, such as number of bytes that are transferred and error string, to the message. You then send this data to the thread handler.
S3PutObjectThread
gets the Uri of an image to be uploaded from its constructor. The upload logic is implemented inside the run()
method. You can send an S3_UPLOAD_START message before the upload starts and an S3_UPLOAD_FINISH message when it finishes. A progress listener is attached to the upload request in order to report transfer progress. When the listener is triggered, you compute the total bytes transferred and send an S3_UPLOAD_PROGRESS message with this number to the main thread.
class S3PutObjectThread extends Thread { private Uri selectedImage; S3PutObjectThread(Uri selectedImage) { this.selectedImage = selectedImage; } @Override public void run() { // Get the file path of the image from selectedImage ... File imageFile = new File(filePath); Message msg; // Notify the main thread that the upload starts msg = handler.obtainMessage(S3_UPLOAD_START); handler.sendMessage(msg); try { // Create a S3 bucket if necessary ... PutObjectRequest por = new PutObjectRequest(bucket, key, imageFile); // Attach a progress listener to the request por.setProgressListener(new ProgressListener() { int total = 0; @Override public void progressChanged(ProgressEvent pe) { total += (int) pe.getBytesTransfered(); // Create a message, and set the total bytes transferred to arg1 Message msg = handler.obtainMessage(S3_UPLOAD_PROGRESS, total, 0); // Send progress update to the main thread handler.sendMessage(msg); } }); // Send the request s3Client.putObject(por); } catch (Exception e) { // Handle exception here. ... } // Upload finishes msg = handler.obtainMessage(S3_UPLOAD_FINISH); handler.sendMessage(msg); } } ... // Create a thread and start it new S3PutObjectThread(selectedImage).start();
Comparison Between Handler and AsyncTask
AsyncTask
is a wrapper of Handler
and Thread
. It allows you to conveniently make asynchronous calls without handling threads and handlers. You only need to focus on the four steps in the execution cycle of AsyncTask
: onPreExecute
, doInBackground
, onProgressUpdate
, and onPostExecute
. However, convenience comes with a price. Here are some limitations.
- An
AsyncTask
instance has to be initiated and executed in the main thread. -
AsyncTask
instances cannot communicate with each other easily. - It’s not easy to schedule
AsyncTask
to run at a certain time in the future. - The execution order of multiple
AsyncTask
instances may not be what you think. Since the arrival ofHoneycomb
(Android 3.0, API level 11), multipleAsyncTask
instances are executed sequentially by default. If you want to change this behavior, you can have them executed onTHREAD_POOL_EXECUTOR
by invokingexecuteOnExecutor
.
Handler
and Thread
are the underlying implementation of AsyncTask
. They don’t have the limitations of AsyncTask
and give you more control over what you want to do.
- The initialization and execution of a
Thread
are not limited to the main thread. They can be done almost everywhere. - Communication between threads won’t be a problem with handlers. If you want to send messages to a thread, you can attach a handler to it. There is a handy class
HandlerThread
that allows you to create a handler for a thread (other than the main thread). -
Handler
has several methods for scheduling messages in the future:sendMessageAtTime
,sendMessageDelayed
,postMessageAtTime
, andpostMessageDelay
.
The choice between AsyncTask
and Handler
may vary, depending on specific requirements, legacy code, or personal taste. I hope that after reading this post you have a better understanding of Handler
and that you properly choose which way to make asynchronous calls in the future.
If you have any questions, please don’t hesitate to post at Mobile Development Forum.
If you like building mobile applications that use cloud services that our customers use on a daily basis, perhaps you would like to join the AWS Mobile SDK and Tools team. We are hiring Software Developers, Web Developers, and Product Managers.