Threading with Handler, Looper and Messages
Most UI applications have one thread, aka Main Thread, to handle UI drawing, respond to events from the system, process user input etc. Android is no different. It's a cardinal rule that this thread should not be burdened further by your CPU hungry or IO thirsty code in your new shiny app. Therefore, you need another thread 🧵. (But be warned. Adding more threads doesn't necessarily make your app faster).
There are several solutions to threading in Android such as:
- Java Thread
- Executor Framework
- Libraries such as Coroutines or Rx since they utilize thread pools in their Dispatchers or Schedulers respectively
- And many more.
In this post, I'll focus on the HanderThread.
The official documentation describes it as a:
Thread that has a Looper.
Let's break this down.
A thread is a trail of execution of a program. We create threads by subclassing the Thread class or implementing the Runnable interface. Both of these ways provide a run method in which we wrap code to be executed. When this code is done, the method returns and the thread goes into the Terminated state. One way to keep the thread running is to provide a loop in the run method that loops indefinitely. This way, the thread can keep on working on computations that share some state updated from some other thread. While this is great, it's usually the cause of all kinds of headaches in multithreading: visibility problems, synchronization problems, etc. Some solutions to such problems include classes in the java.util.concurrent.atomic package,
synchronization block and keyword, ThreadLocal, Locks, Conditions etc.
With a picture of what threads are, let's find out what is a Looper?
This is an ingenious utility class by the folks at Android. It essentially has a
loop method which, simple enough, runs an indefinite loop that fetches queued messages for your thread to work on. So kinda like what's describe in the previous section but with much more functionality baked in to the framework. Find a typical example of the implementation of a thread with a Looper here. A Looper keeps a queue of messages to be processed in a thread.
In summary, a
HandlerThread is a Thread class with a utility class that keeps it alive through a loop that processes incoming messages.
What about the Handler part in its name?
A Handler class is used to schedule messages and runnable objects to be processed at a later time. These messages are enqueued in the Looper's message queue and picked up for processing on the thread associated with the Looper. Therefore the Handler provides us with an api into the Looper's
MessageQueue. You typically override the
handleMessage method to provide the processing actions associated with your message.
With this understanding, we can therefore answer the question above. The HandlerThread has an associated handler in which we can use to enqueue messages. NB: Enqueued runnable objects are wrapped in a Message for scheduling.
This class represents an action with arbitrary data that can be posted to a MessageQueue. For example, when you invoke Toast.show(), this method uses a handler to send a message with the action SHOW that in turn asks the WindowManager to add and display the toast view.
This is the class in charge of extracting and providing the next message to be processed on the thread. Actual
Messages are stored as a
Messages (Message class has a field next that holds the next message in the chain).
Say we want to solve the N-Queens puzzle. The computation complexity of this problem increases exponentially with the increase in the number of Queens. Even for small values of N, we'd need to offload this calculation to a background thread to keep our main thread free. We employ the
HandlerThread for this and also create a handler from the main thread's looper to post results back to the UI thread for display.
Every Android application has a main thread that has an associated
Handler). This thread is kept alive by the
Looper as long as the application is still running. We can employ this technique and these classes to create a background thread messaging system manually, or could just use the already wired-up
HandlerThread class. This class is easy to use and already integrated in to the framework hence applicable in many situations.
One more tool for your threading needs. Consider it next time. Happy coding.