Looking Back Into MessageQueue, Looper and Handler Internal💫
We know Threads are an old man thing and Coroutine is the new guy in town. You may not know about it if you were living under the rock for a good sometimes. For now, we will focus on threads.
A thread is a lightweight subprocess, the smallest unit of processing. It is a separate path of execution. Threads are independent. If there occurs an exception in one thread, it doesn’t affect other threads. It uses a shared memory area. Thread class provides constructors and methods to create and perform operations on a thread. Thread class extends Object class and implements Runnable interface.
By default, a thread starts, execute some work and when it is done it terminates. After it is terminated we can’t use the same thread object again, instead, we have to instantiate a new one.
We can also see this doesn't seem to be the case for the UI thread also called the Main Thread because after the app starts the main thread doesn't terminate even if it doesn't have any work to do instead it will wait for new input, like when we click a button or do something.
Some terms you should know🚀
The mechanism that keeps this thread alive, is called MessageQueue. Instead of just one piece of work, it has a queue of work to execute one after another.
And then there is something called Looper this loops through this MessageQueue and dispatches these messages sequentially, which is simply infinite for loop and useless we quite it in purpose we don't leave this loop, so we don't get to the part where the thread terminates.
Finally, there is a Handler who is responsible for getting these packages of work into the MessageQueue. Here it can not only put these messages at the start or end of the queue instead we can also specify a time or delay to change the order of these messages. It eventually executes the work when It is provided.
How do these things work together?🎠
The Looper loops through this MessageQueue, looking for a message which has a timestamp for now or is already in the past. Then it dispatches this piece of work to the Handler. So handler doesn't only put the work into the queue but also eventually execute it. Then the Lopper goes for the next round and continues.
If there is no message in the MessageQueue that is to be executed right now, then its thread just blocks and waits until there is a message.
When we quite this Looper we get out of the infinite loop, and then the thread terminates.
Thread is just a normal Java class and not android specific. But MessageQueue, Looper and Handler belong to the android framework. This Event Loop concept is not only in android but followed in all GUI frameworks. This is the whole concept behind the HandlerThread class.
Looking Back Into HandlerThread Android Internal💫
A Thread that has a Looper. The Looper can then be used to create Handler s. … Note that just like with a regular…
Making MessageQueue, Looper, Handler From Scratch Work Together🎆
We will start, by creating a new project and adding a simple UI of adding two buttons to start and end the thread. Next will make a class CustomLooper and add the following code for now.
Our thread is now terminated, Now when we click start again the app crashes because we tried to start the same thread again which is not possible.
Presently we have no way to add additional work to the thread, we have to create a whole new object.
Correct Way to do this…🌈
Now comes the Handler, we go back to our CustomLooper class. We add a handler to class, and we know that by default the handler is associated with the thread it is instantiated. So, when we initialize our handler in the run method, this will be executed in the background thread, hence the Handler should be associated with the MessageQueue of the Background Thread.
One more thing before we start our app we have to call Looper.prepare(), since a Handler can only work for threads that have a looper and a message queue, which can be simply done by adding the line. After handler instantiation, we will add Looper.loop(). This order is important. This loop() starts the infinite for loop.
Let’s go back to our MainActivity and use this Thread Handler to add some work to our background thread. For this, I have added two new buttons to the UI.
And to pass Tasks from the MainActvity we can post them as Runnable to the Handler(See the below Image, Click on Start Thread First the add Task A or Task B).
Here you can see that they are executed one after another and not at the same time. This thread will keep running until we kill it on purpose. Which we will do with the help of looper, we will have to expose our looper in CustomLooper to outside
Now go back to the MainActivity.kt in the stop thread method just add
customLooper.looper.quitSafely() quit() will not run any more messages after the current one whereas quitSafely() will run all messages that have already hit the dispatch barrier.
We otherwise just do
Rather than using the public handler that we made from the CustomLooper class, you can simply define a new local Handler, and pass the looper of the CustomLooper otherwise by de
Wow! you have done it✨
Why is it called MessageQueue and not Runnable Queue?🎟️
This question might come to your mind why is it called MessageQueue and not Runnable Queue since we are just passing Runnable. Let’s replace the tasks with messages and pass them to the MessageQueue.
First Image, make a CustomHandler by extending the Handler we will override
handleMessage() which will receive the message and work accordingly. In the second Image, we can see we are passing messages on clicking the button and using
handler.sendMessage() and finally just replacing the handler in CustomLooper with the new handler.
Problems in this Approach🎭
The above is not at all something that you can directly copy paste and use in your, it has multiple quarks. Here are some important points I want to mention here
- After you stop the thread with the
customLooper.looper.quit()Now, if we try to post a new task it shows a warning
sending message to a Handler on a dead threadand when trying to start again, it will crash since like a normal Java Thread we can’t start multiple times.
- If we try to call the Handler before we started this thread, it will crash.
- Another problem is the Runnable inside will create a Potential Memory Leak, with an anonymous object. It has reference to the outer main class. So the Runnable here have reference to Activity. As long we have reference to the Activity can’t be garbage collected. When the activity is destroyed and if the thread is running, since the thread has to reference activity it can’t be garbage collected. One way is to make a static inner class and extend Runnable.
- If you start the thread, don’t quit the Looper and destroy our activity you end up with an infinite long-running zombie thread
I will well appreciate one of these 👏