Threading With HandlerThread in Android

You will learn how to use HandlerThread to receive messages from a Runnable in an Activity and pass them back to a UI handler to update the UI. By Amanjeet Singh.

Leave a rating/review
Download materials
Save for later
Share

Responsiveness is one of the most valuable features of any app. This is even more important in resource-constrained devices like smartphones. Users must be able to interact with the app in a smooth way without falling into flickering or a slow UI. For these reasons, concurrent programming must now be a part of the skillset of every developer.

It’s very important to know that all the interactions with UI components in an Android app happen on a single thread that we call the main thread — or, simply, the UI thread. Experience in other environments (Swing, AWT and others) teaches that the best approach is the one that translates every operation on the UI into a message to a single consumer that runs on the main thread. In Android, you can do this in many ways using different tools. HandlerThread is one of these.

In this tutorial, you’ll develop a handy food-ordering app that needs concurrent programming to execute its tasks. McWenderlich, a live-updating food-ordering app for a restaurant, will process the incoming orders by converting prices from USD to Indian Rupees (INR). The app can also attach additional side orders to a main order. Along the way you’ll learn:

The typical HandlerThread use cases.

  • The core components of the HaMeR framework: Handler, Message and Runnable, and other two relevant classes: Looper and MessageQueue.
  • The role of HandlerThread in the framework.
  • How to create your own HandlerThread implementation to update the UI.
  • How HandlerThread compares with other tools like AsyncTask, Service and IntentService.

Prerequisite: This tutorial assumes you are already familiar with the basics of Android development. If you are completely new to Android development, read through our Beginner Android Series. Other prerequisites include basic knowledge of threads and multi-threading.

Getting Started

As a first step, download the materials for this tutorial by clicking the Download materials button at the top or bottom of the tutorial. Once you’ve downloaded the materials, open the starter project in Android Studio 3.1.3 or later and get familiar with its content.

You should see:

  • FoodListAdapter: An adapter class for the RecyclerView of different food orders containing the order’s name, price in INR and the side order.
  • FoodOrder: A model class for ordered food with three variables: foodName, foodPrice and sideOrder.
  • FoodRunnable: A Runnable implementation, which will allow the creation of orders to send to a background thread, which will do the processing operations.
  • MainActivity: An Activity where you have live updating food orders with their respective details.

Build and run the project. You’ll see a blank screen, which for now just shows an empty RecyclerView:

This isn’t what you want. The UI should display the new orders as soon as our hungry customers create them. You need to manage orders in the background and display them in the list. This is a typical problem that HandlerThread can solve. But before implementing this, you’ll dive into the theory of HandlerThread.

HandlerThread and its Components

In Android, the HaMeR framework allows for interaction between any pair of threads, which can be any background thread and the main thread. In order to avoid race conditions and deadlocks, a thread can execute operations simply reading messages from a queue and executing them in sequence. When thread A wants to interact with thread B, thread A puts a message into the related queue. This can be done using a Handler object.

In more detail, the actors of this framework are:

  • Handler: This is the most important object of the framework because of its double responsibility. It’s bound to a specific Looper for a given thread, and it provides the methods for sending messages to the related MessageQueue. The Handler is also responsible for the actual execution of the Message content.
  • Message: Encapsulates the information about the operation you want to execute on a specific thread.
  • Runnable: Interface that abstracts any operation that a thread can execute.
  • MessageQueue: As the name suggests, this represents the queue of messages that a thread consumes.
  • Looper: This is the object responsible for the loop that checks the MessageQueue to see if there is message to run and send it to a specific Handler. Every thread can have only one Looper.

.

But what’s the role of the HandlerThread class in all of this? As you’ll soon see, it’s a class that simplifies the creation of a Looper and related Handler instances in the case of background threads.

Now, time to start writing code!

Creating Your Own HandlerThread

In Android Studio, right-click on the com.raywenderlich.mcwenderlich root package folder and select New ▸ Kotlin File/Class. Next, name it OrderHandlerThread and select Class for Kind. Make it extend HandlerThread.

The constructor of HandlerThread requires a String as parameter. This String parameter is a name you can use to identify the thread during the debug. You can use the name of the class itself as the identifier, getting something like this:

class OrderHandlerThread : HandlerThread("OrderHandlerThread") {
}

Now, add a UiHandler property to the primary constructor, which is a handler that corresponds to your main thread. This class handles UI changes in MainActivity based on the messages it receives. This class is available for you in the MainActivity class. OrderHandlerThread should now look like this:

class OrderHandlerThread(private var uiHandler: MainActivity.UiHandler) :
    HandlerThread("OrderHandlerThread") {
}

Add two variable declarations, for a Handler and a random value, to the OrderHandlerThread class:

class OrderHandlerThread(private var uiHandler: MainActivity.UiHandler) :
    HandlerThread("OrderHandlerThread") {

  private var handler: Handler? = null
  private val random = Random()
}

Now, add the following two utility methods to this class. The first method convertCurrency will help you convert the currency of foodPrice:

/**
 * Converts the food price from USD to Indian Rupees (INR).
 * 1 USD has been considered as equal to 68.45 INR.
 * @foodPriceInDollars price of the food in USD.
 */
private fun convertCurrency(foodPriceInDollars: Float): Float {
  return foodPriceInDollars * 68.45f
}

The second method attachSideOrder attaches additional random side to each of the upcoming orders:

/**
 * Attaches random side order to the incoming food orders.
 */
private fun attachSideOrder(): String {
  val randomOrder = random.nextInt(3)
  return when (randomOrder) {
    0 -> "Chips"
    1 -> "Salad"
    else -> "Nachos"
  }
}

You need to create three more methods in the OrderHandlerThread class:

  • getHandler prepares the Handler object bound to the corresponding OrderHandlerThread, which you will use to perform the actual operations of converting the currency and attaching a side order to the upcoming foodOrder.
  • sendOrder gets the corresponding foodOrder and passes it to the Handler for operations.
  • onLooperPrepared helps to setup the handler with a looper.

Let’s take a look at those now.

Amanjeet Singh

Contributors

Amanjeet Singh

Author

Massimo Carli

Tech Editor

Sean Stewart

Illustrator

Meng Taing

Final Pass Editor

Eric Soto

Team Lead

Over 300 content creators. Join our team.