In Understanding Concurrency on Android Using HaMeR, we talked about the basics of the HaMeR (Handler, Message, and Runnable) framework. We covered its options, as well as when and how to use it.
Today, we’ll create a simple application to explore the concepts learned. With a hands-on approach, we’ll see how to apply the different possibilities of HaMeR in managing concurrency on Android.
1. The Sample Application
Let’s get to work and post some Runnable and send Message objects on a sample application. To keep it as simple as possible, we’ll explore only the most interesting parts. All resource files and standard activity calls will be ignored here. So I strongly advise you to check out the source code of the sample application with its extensive comments.

The app will consist of:
- Two activities, one for
Runnableanother forMessagecalls - Two
HandlerThreadobjects:-
WorkerThreadto receive and process calls from the UI -
CounterThreadto receiveMessagecalls from theWorkerThread
-
- Some utility classes (to preserve objects during configuration changes and for layout)
2. Posting and Receiving Runnables
Let's begin experimenting with the Handler.post(Runnable) method and its variations, which add a runnable to a MessageQueue associated with a thread. We'll create an activity called RunnableActivity, which communicates with a background thread called WorkerThread.
The RunnableActivity instantiates a background thread called WorkerThread, passing a Handler and a WorkerThread.Callback as parameters. The activity can make calls on WorkerThread to asynchronously download a bitmap and exhibit a toast at a certain time. The results of the tasks done by the worker thread are passed to RunnableActivity by runnables posted on the Handler received by WorkerThread.
2.1 Preparing a Handler for RunnableActivity
On the RunnableActivity we'll create a Handler to be passed to WorkerThread. The uiHandler will be associated with the Looper from the UI thread, since it's being called from that thread.
public class RunnableActivity extends Activity {
// Handler that allows communication between
// the WorkerThread and the Activity
protected Handler uiHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// prepare the UI Handler to send to WorkerThread
uiHandler = new Handler();
}
}
2.2 Declaring WorkerThread and Its Callback Interface
The WorkerThread is a background thread where we'll start different kinds of tasks. It communicates with the user interface using the responseHandler and a callback interface received during its instantiation. The references received from the activities are WeakReference<> type, since an activity could be destroyed and the reference lost.
The class offers an interface that can be implemented by the UI. It also extends HandlerThread, a helper class built on top of Thread that already contains a Looper, and a MessageQueue. Hence it has the correct setup to use the HaMeR framework.
public class WorkerThread extends HandlerThread {
/**
* Interface to facilitate calls on the UI.
*/
public interface Callback {
void loadImage(Bitmap image);
void showToast(String msg);
}
// This Handler will be responsible only
// for posting Runnables on this Thread
private Handler postHandler;
// Handler is received from the MessageActivity and RunnableActivity
// responsible for receiving Runnable calls that will be processed
// on the UI. The callback will help this process.
private WeakReference<Handler> responseHandler;
// Callback from the UI
// it is a WeakReference because it can be invalidated
// during "configuration changes" and other events
private WeakReference<Callback> callback;
private final String imageAUrl =
"https://pixabay.com/static/uploads/photo/2016/08/05/18/28/mobile-phone-1572901_960_720.jpg";
/**
* The constructor receives a Handler and a Callback from the UI
* @param responseHandler in charge of posting the Runnable to the UI
* @param callback works together with the responseHandler
* allowing calls directly on the UI
*/
public WorkerThread(Handler responseHandler, Callback callback) {
super(TAG);
this.responseHandler = new WeakReference<>(responseHandler);
this.callback = new WeakReference<>(callback);
}
}
2.3 Initializing WorkerThread
We need to add a method to WorkerThread to be called by the activities that prepare the thread's postHandler for use. The method needs to be called only after the thread is started.
public class WorkerThread extends HandlerThread {
/**
* Prepare the postHandler.
* It must be called after the thread has started
*/
public void prepareHandler() {
postHandler = new Handler(getLooper());
}
}
On the RunnableActivity we must implement WorkerThread.Callback and initialize the thread so it can be used.
public class RunnableActivity extends Activity
implements WorkerThread.Callback {
// BackgroundThread responsible for downloading the image
protected WorkerThread workerThread;
/**
* Initialize the {@link WorkerThread} instance
* only if it hasn't been initialized yet.
*/
public void initWorkerThread(){
if ( workerThread == null ) {
workerThread = new WorkerThread(uiHandler, this);
workerThread.start();
workerThread.prepareHandler();
}
}
/**
* set the image downloaded on bg thread to the imageView
*/
@Override
public void loadImage(Bitmap image) {
myImage.setImageBitmap(image);
}
@Override
public void showToast(final String msg) {
// to be implemented
}
}
2.4 Using Handler.post() on the WorkerThread
The WorkerThread.downloadWithRunnable() method downloads a bitmap and sends it to RunnableActivity to be displayed in an image View. It illustrates two basic uses of the Handler.post(Runnable run) command:
-
To allow a Thread to post a Runnable object to a MessageQueue associated with itself when
.post()is called on a Handler associated with the Thread's Looper. - To allow communication with other Threads, when
.post()is called on a Handler associated with other Thread's Looper.

- The
WorkerThread.downloadWithRunnable()method posts aRunnableto theWorkerThread'sMessageQueueusing thepostHandler, aHandlerassociated withWorkThread'sLooper. - When the runnable is processed, it downloads a
Bitmapon theWorkerThread. - After the bitmap is downloaded, the
responseHandler, a handler associated with the UI thread, is used to post a runnable on theRunnableActivitycontaining the bitmap. - The runnable is processed, and the
WorkerThread.Callback.loadImageis used to exhibit the downloaded image on anImageView.
public class WorkerThread extends HandlerThread {
/**
* post a Runnable to the WorkerThread
* Download a bitmap and sends the image
* to the UI {@link RunnableActivity}
* using the {@link #responseHandler} with
* help from the {@link #callback}
*/
public void downloadWithRunnable() {
// post Runnable to WorkerThread
postHandler.post(new Runnable() {
@Override
public void run() {
try {
// sleeps for 2 seconds to emulate long running operation
TimeUnit.SECONDS.sleep(2);
// Download image and sends to UI
downloadImage(imageAUrl);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
/**
* Download a bitmap using its url and
* send to the UI the image downloaded
*/
private void downloadImage(String urlStr){
// Create a connection
HttpURLConnection connection = null;
try {
URL url = new URL(urlStr);
connection = (HttpURLConnection) url.openConnection();
// get the stream from the url
InputStream in = new BufferedInputStream(connection.getInputStream());
final Bitmap bitmap = BitmapFactory.decodeStream(in);
if ( bitmap != null ) {
// send the bitmap downloaded and a feedback to the UI
loadImageOnUI( bitmap );
} else {
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if ( connection != null )
connection.disconnect();
}
}
/**
* sends a Bitmap to the ui
* posting a Runnable to the {@link #responseHandler}
* and using the {@link Callback}
*/
private void loadImageOnUI(final Bitmap image){
Log.d(TAG, "loadImageOnUI("+image+")");
if (checkResponse() ) {
responseHandler.get().post(
new Runnable() {
@Override
public void run() {
callback.get().loadImage(image);
}
}
);
}
}
// verify if responseHandler is available
// if not the Activity is passing by some destruction event
private boolean checkResponse(){
return responseHandler.get() != null;
}
}
2.5 Using Handler.postAtTime() and Activity.runOnUiThread()
The WorkerThread.toastAtTime() schedules a task to be executed at a certain time, exhibiting a Toast to the user. The method illustrates the use of the Handler.postAtTime() and the Activity.runOnUiThread().
-
Handler.postAtTime(Runnable run, long uptimeMillis)posts a runnable at a given time.
-
Activity.runOnUiThread(Runnable run)uses the default UI handler to post a runnable to the main thread.
public class WorkerThread extends HandlerThread {
/**
* show a Toast on the UI.
* schedules the task considering the current time.
* It could be scheduled at any time, we're
* using 5 seconds to facilitates the debugging
*/
public void toastAtTime(){
Log.d(TAG, "toastAtTime(): current - " + Calendar.getInstance().toString());
// seconds to add on current time
int delaySeconds = 5;
// testing using a real date
Calendar scheduledDate = Calendar.getInstance();
// setting a future date considering the delay in seconds define
// we're using this approach just to facilitate the testing.
// it could be done using a user defined date also
scheduledDate.set(
scheduledDate.get(Calendar.YEAR),
scheduledDate.get(Calendar.MONTH),
scheduledDate.get(Calendar.DAY_OF_MONTH),
scheduledDate.get(Calendar.HOUR_OF_DAY),
scheduledDate.get(Calendar.MINUTE),
scheduledDate.get(Calendar.SECOND) + delaySeconds
);
Log.d(TAG, "toastAtTime(): scheduling at - " + scheduledDate.toString());
long scheduled = calculateUptimeMillis(scheduledDate);
// posting Runnable at specific time
postHandler.postAtTime(
new Runnable() {
@Override
public void run() {
if ( callback != null ) {
callback.get().showToast(
"Toast called using 'postAtTime()'."
);
}
}
}, scheduled);
}
/**
* Calculates the {@link SystemClock#uptimeMillis()} to
* a given Calendar date.
*/
private long calculateUptimeMillis(Calendar calendar){
long time = calendar.getTimeInMillis();
long currentTime = Calendar.getInstance().getTimeInMillis();
long diff = time - currentTime;
return SystemClock.uptimeMillis() + diff;
}
}
public class RunnableActivity extends Activity
implements WorkerThread.Callback {
/**
* Callback from {@link WorkerThread}
* Uses {@link #runOnUiThread(Runnable)} to illustrate
* such method
*/
@Override
public void showToast(final String msg) {
Log.d(TAG, "showToast("+msg+")");
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_LONG).show();
}
});
}
}
3. Sending Messages With the MessageActivity & WorkerThread
Next, let's explore some different ways of using MessageActivity to send and process Message objects. The MessageActivity instantiates WorkerThread, passing a Handler as a parameter. The WorkerThread has some public methods with tasks to be called by the activity to download a bitmap, download a random bitmap, or exhibit a Toast after some delayed time. The results of all those operations are sent back to MessageActivity using Message objects sent by the responseHandler.
3.1 Preparing the Response Handler From MessageActivity
As in the RunnableActivity, in the MessageActivity we'll have to instantiate and initialize a WorkerThread sending a Handler to receive data from the background thread. However, this time we won't implement WorkerThread.Callback; instead, we'll receive responses from the WorkerThread exclusively by Message objects.
Since most of the MessageActivity and RunnableActivity code is basically the same, we'll concentrate only on the uiHandler preparation, which will be sent to WorkerThread to receive messages from it.
First, let's provide some int keys to be used as identifiers to the Message objects.
public class MessageActivity extends Activity {
// Message identifier used on Message.what() field
public static final int KEY_MSG_IMAGE = 2;
public static final int KEY_MSG_PROGRESS = 3;
public static final int KEY_MSG_TOAST = 4;
}
On MessageHandler implementation, we'll have to extend Handler and implement the handleMessage(Message) method, where all messages will be processed. Notice that we're fetching Message.what to identify the message, and we're also getting different kinds of data from Message.obj. Let's quickly review the most important Message properties before diving into the code.
-
Message.what:intidentifying theMessage -
Message.arg1:intarbitrary argument -
Message.arg2:intarbitrary argument -
Message.obj:Objectto store different kinds of data
public class MessageActivity extends Activity {
/**
* Handler responsible to manage communication
* from the {@link WorkerThread}. It sends Messages
* back to the {@link MessageActivity} and handle
* those Messages
*/
public class MessageHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
// handle image
case KEY_MSG_IMAGE:{
Bitmap bmp = (Bitmap) msg.obj;
myImage.setImageBitmap(bmp);
break;
}
// handle progressBar calls
case KEY_MSG_PROGRESS: {
if ( (boolean) msg.obj )
progressBar.setVisibility(View.VISIBLE);
else
progressBar.setVisibility(View.GONE);
break;
}
// handle toast sent with a Message delay
case KEY_MSG_TOAST:{
String msgText = (String)msg.obj;
Toast.makeText(getApplicationContext(), msgText, Toast.LENGTH_LONG ).show();
break;
}
}
}
}
// Handler that allows communication between
// the WorkerThread and the Activity
protected MessageHandler uiHandler;
}
3.2 Sending Messages With WorkerThread
Now let's get back to the WorkerThread class. We'll add some code to download a specific bitmap and also code to download a random one. To accomplish those tasks, we'll send Message objects from the WorkerThread to itself and send the results back to MessageActivity using exactly the same logic applied earlier for the RunnableActivity.
First we need to extend the Handler to process the downloaded messages.
public class WorkerThread extends HandlerThread {
// send and processes download Messages on the WorkerThread
private HandlerMsgImgDownloader handlerMsgImgDownloader;
/**
* Keys to identify the keys of {@link Message#what}
* from Messages sent by the {@link #handlerMsgImgDownloader}
*/
private final int MSG_DOWNLOAD_IMG = 0; // msg that download a single img
private final int MSG_DOWNLOAD_RANDOM_IMG = 1; // msg that download random img
/**
* Handler responsible for managing the image download
* It send and handle Messages identifying then using
* the {@link Message#what}
* {@link #MSG_DOWNLOAD_IMG} : single image
* {@link #MSG_DOWNLOAD_RANDOM_IMG} : random image
*/
private class HandlerMsgImgDownloader extends Handler {
private HandlerMsgImgDownloader(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
showProgressMSG(true);
switch ( msg.what ) {
case MSG_DOWNLOAD_IMG: {
// receives a single url and downloads it
String url = (String) msg.obj;
downloadImageMSG(url);
break;
}
case MSG_DOWNLOAD_RANDOM_IMG: {
// receives a String[] with multiple urls
// downloads a image randomly
String[] urls = (String[]) msg.obj;
Random random = new Random();
String url = urls[random.nextInt(urls.length)];
downloadImageMSG(url);
}
}
showProgressMSG(false);
}
}
}
The downloadImageMSG(String url) method is basically the same as the downloadImage(String url) method. The only difference is that the first sends the downloaded bitmap back to the UI by sending a message using the responseHandler.
public class WorkerThread extends HandlerThread {
/**
* Download a bitmap using its url and
* display it to the UI.
* The only difference with {@link #downloadImage(String)}
* is that it sends the image back to the UI
* using a Message
*/
private void downloadImageMSG(String urlStr){
// Create a connection
HttpURLConnection connection = null;
try {
URL url = new URL(urlStr);
connection = (HttpURLConnection) url.openConnection();
// get the stream from the url
InputStream in = new BufferedInputStream(connection.getInputStream());
final Bitmap bitmap = BitmapFactory.decodeStream(in);
if ( bitmap != null ) {
// send the bitmap downloaded and a feedback to the UI
loadImageOnUIMSG( bitmap );
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if ( connection != null )
connection.disconnect();
}
}
}
The loadImageOnUIMSG(Bitmap image) is responsible for sending a message with the downloaded bitmap to MessageActivity.
/**
* sends a Bitmap to the ui
* sending a Message to the {@link #responseHandler}
*/
private void loadImageOnUIMSG(final Bitmap image){
if (checkResponse() ) {
sendMsgToUI(
responseHandler.get().obtainMessage(MessageActivity.KEY_MSG_IMAGE, image)
);
}
}
/**
* Show/Hide progressBar on the UI.
* It uses the {@link #responseHandler} to
* send a Message on the UI
*/
private void showProgressMSG(boolean show){
Log.d(TAG, "showProgressMSG()");
if ( checkResponse() ) {
sendMsgToUI(
responseHandler.get().obtainMessage(MessageActivity.KEY_MSG_PROGRESS, show)
);
}
}
Notice that instead of creating a Message object from scratch, we're using the Handler.obtainMessage(int what, Object obj) method to retrieve a Message from the global pool, saving some resources. It's also important to note that we're calling the obtainMessage() on the responseHandler, obtaining a Message associated with MessageActivity's Looper. There are two ways to retrieve a Message from the global pool: Message.obtain() and Handler.obtainMessage().
The only thing left to do on the image download task is to provide the methods to send a Message to WorkerThread to start the download process. Notice that this time we'll call Message.obtain(Handler handler, int what, Object obj) on handlerMsgImgDownloader, associating the message with WorkerThread's looper.
/**
* sends a Message to the current Thread
* using the {@link #handlerMsgImgDownloader}
* to download a single image.
*/
public void downloadWithMessage(){
Log.d(TAG, "downloadWithMessage()");
showOperationOnUIMSG("Sending Message...");
if ( handlerMsgImgDownloader == null )
handlerMsgImgDownloader = new HandlerMsgImgDownloader(getLooper());
Message message = Message.obtain(handlerMsgImgDownloader, MSG_DOWNLOAD_IMG,imageBUrl);
handlerMsgImgDownloader.sendMessage(message);
}
/**
* sends a Message to the current Thread
* using the {@link #handlerMsgImgDownloader}
* to download a random image.
*/
public void downloadRandomWithMessage(){
Log.d(TAG, "downloadRandomWithMessage()");
showOperationOnUIMSG("Sending Message...");
if ( handlerMsgImgDownloader == null )
handlerMsgImgDownloader = new HandlerMsgImgDownloader(getLooper());
Message message = Message.obtain(handlerMsgImgDownloader, MSG_DOWNLOAD_RANDOM_IMG, imagesUrls);
handlerMsgImgDownloader.sendMessage(message);
}
Another interesting possibility is sending Message objects to be processed at a later time with the command Message.sendMessageDelayed(Message msg, long timeMillis).
/**
* Show a Toast after a delayed time.
*
* send a Message with delayed time on the WorkerThread
* and sends a new Message to {@link MessageActivity}
* with a text after the message is processed
*/
public void startMessageDelay(){
// message delay
long delay = 5000;
String msgText = "Hello from WorkerThread!";
// Handler responsible for sending Message to WorkerThread
// using Handler.Callback() to avoid the need to extend the Handler class
Handler handler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
responseHandler.get().sendMessage(
responseHandler.get().obtainMessage(MessageActivity.KEY_MSG_TOAST, msg.obj)
);
return true;
}
});
// sending message
handler.sendMessageDelayed(
handler.obtainMessage(0,msgText),
delay
);
}
We created a Handler expressly for sending the delayed message. Instead of extending the Handler class, we took the route of instantiating a Handler by using the Handler.Callback interface, for which we implemented the handleMessage(Message msg) method to process the delayed Message.
4. Conclusion
You've seen enough code by now to understand how to apply the basic HaMeR framework concepts to manage concurrency on Android. There are some other interesting features of the final project stored on GitHub, and I strongly advise you to check it out.
Finally, I have some last considerations that you should keep in mind:
-
Don't forget to consider Android's Activity lifecycle when working with HaMeR and Threads in general. Otherwise, your app may fail when the thread tries to access activities that have been destroyed due to configuration changes or for other reasons. A common solution is to use a
RetainedFragmentto store the thread and populate the background thread with the activity's reference every time the activity is destroyed. Take a look at the solution in the final project on GitHub. - Tasks that run due to
RunnableandMessageobjects processed onHandlersdon't run asynchronously. They'll run synchronously on the thread associated with the handler. To make it asynchronous, you'll need to create another thread, send/post theMessage/Runnableobject on it, and receive the results at the appropriate time.
As you can see, the HaMeR framework has lots of different possibilities, and it's a fairly open solution with a lot of options for managing concurrency on Android. These characteristics can be advantages over AsyncTask, depending on your needs. Explore more of the framework and read the documentation, and you'll create great things with it.
See you soon!

Comments