1. Code
  2. Coding Fundamentals

Practical Concurrency on Android With HaMeR

In this tutorial we’ll explore the HaMeR (Handler, Message and Runnable) framework, one of the most powerful concurrency models available on Android. With a hands-on approach, you’ll see how to apply the different possibilities of HaMeR in managing concurrency on Android.
Scroll to top

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. 

HaMeR sample applicationHaMeR sample applicationHaMeR sample application

The app will consist of:

  • Two activities, one for Runnable another for Message calls
  • Two HandlerThread objects:
    • WorkerThread to receive and process calls from the UI
    • CounterThread to receive Message calls from the WorkerThread
  • 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.

1
public class RunnableActivity extends Activity {
2
3
    // Handler that allows communication between

4
    // the WorkerThread and the Activity

5
    protected Handler uiHandler;
6
    
7
    @Override
8
    protected void onCreate(Bundle savedInstanceState) {
9
        super.onCreate(savedInstanceState);
10
        setContentView(R.layout.activity_main);
11
12
        // prepare the UI Handler to send to WorkerThread

13
        uiHandler = new Handler();
14
    } 
15
}

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.

1
public class WorkerThread extends HandlerThread {
2
    /**

3
     * Interface to facilitate calls on the UI.

4
     */
5
    public interface Callback {
6
        void loadImage(Bitmap image);
7
        void showToast(String msg);
8
    }
9
    
10
    // This Handler will be responsible only

11
    // for posting Runnables on this Thread

12
    private Handler postHandler;
13
14
    // Handler is received from the MessageActivity and RunnableActivity

15
    // responsible for receiving Runnable calls that will be processed

16
    // on the UI. The callback will help this process.

17
    private WeakReference<Handler> responseHandler;
18
    
19
    // Callback from the UI

20
    // it is a WeakReference because it can be invalidated

21
    // during "configuration changes" and other events

22
    private WeakReference<Callback> callback;
23
    
24
    private final String imageAUrl =
25
            "https://pixabay.com/static/uploads/photo/2016/08/05/18/28/mobile-phone-1572901_960_720.jpg";
26
            
27
    /**

28
     * The constructor receives a Handler and a Callback from the UI

29
     * @param responseHandler   in charge of posting the Runnable to the UI

30
     * @param callback          works together with the responseHandler

31
     *                          allowing calls directly on the UI

32
     */
33
    public WorkerThread(Handler responseHandler, Callback callback) {
34
        super(TAG);
35
        this.responseHandler = new WeakReference<>(responseHandler);
36
        this.callback = new WeakReference<>(callback);
37
    }
38
}

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.

1
public class WorkerThread extends HandlerThread {
2
    /**

3
     * Prepare the postHandler.

4
     * It must be called after the thread has started

5
     */
6
    public void prepareHandler() {
7
        postHandler = new Handler(getLooper());
8
    }
9
}

On the RunnableActivity we must implement WorkerThread.Callback and initialize the thread so it can be used.

1
public class RunnableActivity extends Activity
2
        implements WorkerThread.Callback {
3
        
4
    // BackgroundThread responsible for downloading the image

5
    protected WorkerThread workerThread;
6
    
7
    /**

8
     * Initialize the {@link WorkerThread} instance

9
     * only if it hasn't been initialized yet.

10
     */
11
    public void initWorkerThread(){
12
        if ( workerThread == null ) {
13
            workerThread = new WorkerThread(uiHandler, this);
14
            workerThread.start();
15
            workerThread.prepareHandler();
16
        }
17
    }
18
    
19
    /**

20
     * set the image downloaded on bg thread to the imageView

21
     */
22
    @Override
23
    public void loadImage(Bitmap image) {
24
        myImage.setImageBitmap(image);
25
    }
26
    
27
    @Override
28
    public void showToast(final String msg) {
29
        // to be implemented

30
    }
31
}

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.
  1. The WorkerThread.downloadWithRunnable() method posts a Runnable to the WorkerThread's MessageQueue using the postHandler, a Handler associated with WorkThread's Looper.
  2. When the runnable is processed, it downloads a Bitmap on the WorkerThread.
  3. After the bitmap is downloaded, the responseHandler, a handler associated with the UI thread, is used to post a runnable on the RunnableActivity containing the bitmap.
  4. The runnable is processed, and the WorkerThread.Callback.loadImage is used to exhibit the downloaded image on an ImageView.
1
public class WorkerThread extends HandlerThread {
2
3
    /**

4
     * post a Runnable to the WorkerThread

5
     * Download a bitmap and sends the image

6
     * to the UI {@link RunnableActivity}

7
     * using the {@link #responseHandler} with

8
     * help from the {@link #callback}

9
     */
10
    public void downloadWithRunnable() {
11
        // post Runnable to WorkerThread

12
        postHandler.post(new Runnable() {
13
            @Override
14
            public void run() {
15
                try {
16
                    // sleeps for 2 seconds to emulate long running operation

17
                    TimeUnit.SECONDS.sleep(2);
18
                    // Download image and sends to UI

19
                    downloadImage(imageAUrl);
20
                } catch (InterruptedException e) {
21
                    e.printStackTrace();
22
                }
23
            }
24
        });
25
    }
26
    
27
    /**

28
     * Download a bitmap using its url and

29
     * send to the UI the image downloaded

30
     */
31
    private void downloadImage(String urlStr){
32
        // Create a connection

33
        HttpURLConnection connection = null;
34
        try {
35
            URL url = new URL(urlStr);
36
            connection = (HttpURLConnection) url.openConnection();
37
38
            // get the stream from the url

39
            InputStream in = new BufferedInputStream(connection.getInputStream());
40
            final Bitmap bitmap = BitmapFactory.decodeStream(in);
41
            if ( bitmap != null ) {
42
                // send the bitmap downloaded and a feedback to the UI

43
                loadImageOnUI( bitmap );
44
            } else {
45
            }
46
        } catch (IOException e) {
47
            e.printStackTrace();
48
        } finally {
49
            if ( connection != null )
50
                connection.disconnect();
51
        }
52
    }
53
    
54
    /**

55
     * sends a Bitmap to the ui

56
     * posting a Runnable to the {@link #responseHandler}

57
     * and using the {@link Callback}

58
     */
59
    private void loadImageOnUI(final Bitmap image){
60
        Log.d(TAG, "loadImageOnUI("+image+")");
61
        if (checkResponse() ) {
62
            responseHandler.get().post(
63
                    new Runnable() {
64
                        @Override
65
                        public void run() {
66
                            callback.get().loadImage(image);
67
                        }
68
                    }
69
            );
70
        }
71
    }
72
    
73
    // verify if responseHandler is available

74
    // if not the Activity is passing by some destruction event

75
    private boolean checkResponse(){
76
        return responseHandler.get() != null;
77
    }
78
}

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.
1
public class WorkerThread extends HandlerThread {
2
3
    /**

4
     * show a Toast on the UI.

5
     * schedules the task considering the current time.

6
     * It could be scheduled at any time, we're

7
     * using 5 seconds to facilitates the debugging

8
     */
9
    public void toastAtTime(){
10
        Log.d(TAG, "toastAtTime(): current - " + Calendar.getInstance().toString());
11
12
        // seconds to add on current time

13
        int delaySeconds = 5;
14
15
        // testing using a real date

16
        Calendar scheduledDate = Calendar.getInstance();
17
        // setting a future date considering the delay in seconds define

18
        // we're using this approach just to facilitate the testing.

19
        // it could be done using a user defined date also

20
        scheduledDate.set(
21
                scheduledDate.get(Calendar.YEAR),
22
                scheduledDate.get(Calendar.MONTH),
23
                scheduledDate.get(Calendar.DAY_OF_MONTH),
24
                scheduledDate.get(Calendar.HOUR_OF_DAY),
25
                scheduledDate.get(Calendar.MINUTE),
26
                scheduledDate.get(Calendar.SECOND) + delaySeconds
27
        );
28
        Log.d(TAG, "toastAtTime(): scheduling at - " + scheduledDate.toString());
29
        long scheduled = calculateUptimeMillis(scheduledDate);
30
31
        // posting Runnable at specific time

32
        postHandler.postAtTime(
33
                new Runnable() {
34
            @Override
35
            public void run() {
36
                if ( callback != null ) {
37
                    callback.get().showToast(
38
                            "Toast called using 'postAtTime()'."
39
                    );
40
                }
41
            }
42
        }, scheduled);
43
    }
44
45
    /**

46
     * Calculates the {@link SystemClock#uptimeMillis()} to

47
     * a given Calendar date.

48
     */
49
    private long calculateUptimeMillis(Calendar calendar){
50
        long time = calendar.getTimeInMillis();
51
        long currentTime = Calendar.getInstance().getTimeInMillis();
52
        long diff = time - currentTime;
53
        return SystemClock.uptimeMillis() + diff;
54
    }
55
}
1
public class RunnableActivity extends Activity
2
        implements WorkerThread.Callback {
3
    /**

4
     * Callback from {@link WorkerThread}

5
     * Uses {@link #runOnUiThread(Runnable)} to illustrate

6
     * such method

7
     */
8
    @Override
9
    public void showToast(final String msg) {
10
        Log.d(TAG, "showToast("+msg+")");
11
        runOnUiThread(new Runnable() {
12
            @Override
13
            public void run() {
14
                Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_LONG).show();
15
            }
16
        });
17
    }
18
}

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.

1
public class MessageActivity extends Activity {
2
    // Message identifier used on Message.what() field

3
    public static final int KEY_MSG_IMAGE       = 2;
4
    public static final int KEY_MSG_PROGRESS    = 3;
5
    public static final int KEY_MSG_TOAST       = 4;   
6
}

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.whatint identifying the Message
  • Message.arg1int arbitrary argument
  • Message.arg2int arbitrary argument
  • Message.objObject to store different kinds of data
1
public class MessageActivity extends Activity {
2
    /**

3
     * Handler responsible to manage communication

4
     * from the {@link WorkerThread}. It sends Messages

5
     * back to the {@link MessageActivity} and handle

6
     * those Messages

7
     */
8
    public class MessageHandler extends Handler {
9
        @Override
10
        public void handleMessage(Message msg) {
11
            switch (msg.what) {
12
                // handle image

13
                case KEY_MSG_IMAGE:{
14
                    Bitmap bmp = (Bitmap) msg.obj;
15
                    myImage.setImageBitmap(bmp);
16
                    break;
17
                }
18
                // handle progressBar calls

19
                case KEY_MSG_PROGRESS: {
20
                    if ( (boolean) msg.obj )
21
                        progressBar.setVisibility(View.VISIBLE);
22
                    else
23
                        progressBar.setVisibility(View.GONE);
24
                    break;
25
                }
26
27
                // handle toast sent with a Message delay

28
                case KEY_MSG_TOAST:{
29
                    String msgText = (String)msg.obj;
30
                    Toast.makeText(getApplicationContext(), msgText, Toast.LENGTH_LONG ).show();
31
                    break;
32
                }
33
            }
34
        }
35
    }
36
    
37
    // Handler that allows communication between

38
    // the WorkerThread and the Activity

39
    protected MessageHandler uiHandler;   
40
}
41
    

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.

1
public class WorkerThread extends HandlerThread {
2
    // send and processes download Messages on the WorkerThread

3
    private HandlerMsgImgDownloader handlerMsgImgDownloader;
4
    
5
    /**

6
     * Keys to identify the keys of {@link Message#what}

7
     * from Messages sent by the {@link #handlerMsgImgDownloader}

8
     */
9
    private final int MSG_DOWNLOAD_IMG = 0;         // msg that download a single img

10
    private final int MSG_DOWNLOAD_RANDOM_IMG = 1;  // msg that download random img

11
    
12
    /**

13
     * Handler responsible for managing the image download

14
     * It send and handle Messages identifying then using

15
     * the {@link Message#what}

16
     *      {@link #MSG_DOWNLOAD_IMG} : single image

17
     *      {@link #MSG_DOWNLOAD_RANDOM_IMG} : random image

18
     */
19
    private class HandlerMsgImgDownloader extends Handler {
20
        private HandlerMsgImgDownloader(Looper looper) {
21
            super(looper);
22
        }
23
24
        @Override
25
        public void handleMessage(Message msg) {
26
            showProgressMSG(true);
27
            switch ( msg.what ) {
28
                case MSG_DOWNLOAD_IMG: {
29
                    // receives a single url and downloads it

30
                    String url = (String) msg.obj;
31
                    downloadImageMSG(url);
32
                    break;
33
                }
34
                case MSG_DOWNLOAD_RANDOM_IMG: {
35
                    // receives a String[] with multiple urls

36
                    // downloads a image randomly

37
                    String[] urls = (String[]) msg.obj;
38
                    Random random = new Random();
39
                    String url = urls[random.nextInt(urls.length)];
40
                    downloadImageMSG(url);
41
                }
42
            }
43
            showProgressMSG(false);
44
        }
45
    }
46
}

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.

1
public class WorkerThread extends HandlerThread {
2
    /**

3
     * Download a bitmap using its url and

4
     * display it to the UI.

5
     * The only difference with {@link #downloadImage(String)}

6
     * is that it sends the image back to the UI

7
     * using a Message

8
     */
9
    private void downloadImageMSG(String urlStr){
10
        // Create a connection

11
        HttpURLConnection connection = null;
12
        try {
13
            URL url = new URL(urlStr);
14
            connection = (HttpURLConnection) url.openConnection();
15
16
            // get the stream from the url

17
            InputStream in = new BufferedInputStream(connection.getInputStream());
18
            final Bitmap bitmap = BitmapFactory.decodeStream(in);
19
            if ( bitmap != null ) {
20
                // send the bitmap downloaded and a feedback to the UI

21
                loadImageOnUIMSG( bitmap );
22
            } 
23
            
24
        } catch (IOException e) {
25
            e.printStackTrace();
26
        } finally {
27
            if ( connection != null )
28
                connection.disconnect();
29
        }
30
    }
31
}

The loadImageOnUIMSG(Bitmap image) is responsible for sending a message with the downloaded bitmap to MessageActivity

1
    /**

2
     * sends a Bitmap to the ui

3
     * sending a Message to the {@link #responseHandler}

4
     */
5
    private void loadImageOnUIMSG(final Bitmap image){
6
        if (checkResponse() ) {
7
            sendMsgToUI(
8
                    responseHandler.get().obtainMessage(MessageActivity.KEY_MSG_IMAGE, image)
9
            );
10
        }
11
    }
12
    
13
    /**

14
     * Show/Hide progressBar on the UI.

15
     * It uses the {@link #responseHandler} to

16
     * send a Message on the UI

17
     */
18
    private void showProgressMSG(boolean show){
19
        Log.d(TAG, "showProgressMSG()");
20
        if ( checkResponse() ) {
21
            sendMsgToUI(
22
                    responseHandler.get().obtainMessage(MessageActivity.KEY_MSG_PROGRESS, show)
23
            );
24
        }
25
    }

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.

1
    /**

2
     * sends a Message to the current Thread

3
     * using the {@link #handlerMsgImgDownloader}

4
     * to download a single image.

5
     */
6
    public void downloadWithMessage(){
7
        Log.d(TAG, "downloadWithMessage()");
8
        showOperationOnUIMSG("Sending Message...");
9
        if ( handlerMsgImgDownloader == null )
10
            handlerMsgImgDownloader = new HandlerMsgImgDownloader(getLooper());
11
        Message message = Message.obtain(handlerMsgImgDownloader, MSG_DOWNLOAD_IMG,imageBUrl);
12
        handlerMsgImgDownloader.sendMessage(message);
13
    }
14
15
    /**

16
     * sends a Message to the current Thread

17
     * using the {@link #handlerMsgImgDownloader}

18
     * to download a random image.

19
     */
20
    public void downloadRandomWithMessage(){
21
        Log.d(TAG, "downloadRandomWithMessage()");
22
        showOperationOnUIMSG("Sending Message...");
23
        if ( handlerMsgImgDownloader == null )
24
            handlerMsgImgDownloader = new HandlerMsgImgDownloader(getLooper());
25
        Message message = Message.obtain(handlerMsgImgDownloader, MSG_DOWNLOAD_RANDOM_IMG, imagesUrls);
26
        handlerMsgImgDownloader.sendMessage(message);
27
    }

Another interesting possibility is sending Message objects to be processed at a later time with the command Message.sendMessageDelayed(Message msg, long timeMillis).

1
/**

2
 * Show a Toast after a delayed time.

3
 *

4
 * send a Message with delayed time on the WorkerThread

5
 * and sends a new Message to {@link MessageActivity}

6
 * with a text after the message is processed

7
 */
8
public void startMessageDelay(){
9
    // message delay

10
    long delay = 5000;
11
    String msgText = "Hello from WorkerThread!";
12
13
    // Handler responsible for sending Message to WorkerThread

14
    // using Handler.Callback() to avoid the need to extend the Handler class

15
    Handler handler = new Handler(new Handler.Callback() {
16
        @Override
17
        public boolean handleMessage(Message msg) {
18
            responseHandler.get().sendMessage(
19
                    responseHandler.get().obtainMessage(MessageActivity.KEY_MSG_TOAST, msg.obj)
20
            );
21
            return true;
22
        }
23
    });
24
25
    // sending message

26
    handler.sendMessageDelayed(
27
            handler.obtainMessage(0,msgText),
28
            delay
29
    );
30
}

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 RetainedFragment to 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 Runnable and Message objects processed on Handlers don'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 the Message/Runnable object 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!