A journey on the Android Main Thread — PSVM
From PSVM to loopers and handlers
Written by Pierre-Yves Ricau.
Heads up, we’ve moved! If you’d like to continue keeping up with the latest technical content from Square please visit us at our new home https://developer.squareup.com/blog
There is an article on coding horror about why we should learn to read the source. One of the great aspects of Android is its open source nature.
When facing bugs that were related to how we interact with the main thread, I decided to get a closer look at what the main thread really is. This article describes the first part of my journey.
PSVM
public class BigBang {
public static void main(String... args) {
// The Java universe starts here.
}
}
All Java programs start with a call to a public static void main() method. This is true for Java Desktop programs, JEE servlet containers, and Android applications.
When the Android system boots, it starts a Linux process called ZygoteInit. This process is a Dalvik VM that loads the most common classes of the Android SDK on a thread, and then waits.
When starting a new Android application, the Android system forks the ZygoteInit process. The thread in the child fork stops waiting, and calls ActivityThread.main().
According to Wikipedia, a zygote is a fertilized biological cell.
Loopers
Before going any further, we need to look at the Looper class.
Using a looper is a good way to dedicate one thread to process messages serially.
Each looper has a queue of Message objects (a MessageQueue).
A looper has a loop() method that will process each message in the queue, and block when the queue is empty.
The Looper.loop() method code is similar to this:
void loop() {
while(true) {
Message message = queue.next(); // blocks if empty.
dispatchMessage(message);
message.recycle();
}
}
Each looper is associated with one thread. To create a new looper and associate it to the current thread, you must call Looper.prepare(). The loopers are stored in a static ThreadLocal in the Looper class. You can retrieve the Looper associated to the current thread by calling Looper.myLooper().
The HandlerThread class does everything for you:
HandlerThread thread = new HandlerThread("SquareHandlerThread");
thread.start(); // starts the thread.
Looper looper = thread.getLooper();
Its code is similar to this:
class HandlerThread extends Thread {
Looper looper;
public void run() {
Looper.prepare(); // Create a Looper and store it in a ThreadLocal.
looper = Looper.myLooper(); // Retrieve the looper instance from the ThreadLocal, for later use.
Looper.loop(); // Loop forever.
}
}
Handlers
A handler is the natural companion to a looper.
A handler has two purposes:
- Send messages to a looper message queue from any thread.
- Handle messages dequeued by a looper on the thread associated to that looper.
// Each handler is associated to one looper.
Handler handler = new Handler(looper) {
public void handleMessage(Message message) {
// Handle the message on the thread associated to the given looper.
if (message.what == DO_SOMETHING) {
// do something
}
}
};// Create a new message associated to that handler.
Message message = handler.obtainMessage(DO_SOMETHING);// Add the message to the looper queue.
// Can be called from any thread.
handler.sendMessage(message);
You can associate multiple handlers to one looper. The looper delivers the message to message.target.
A popular and simpler way to use a handler is to post a Runnable:
// Create a message containing a reference to the runnable and add it to the looper queue
handler.post(new Runnable() {
public void run() {
// Runs on the thread associated to the looper associated to that handler.
}
});
A handler can also be created without providing any looper:
// DON'T DO THIS
Handler handler = new Handler();
The handler no argument constructor calls Looper.myLooper() and retrieves the looper associated with the current thread. This may or may not be the thread you actually want the handler to be associated with.
Most of the time, you just want to create a handler to post on the main thread:
Handler handler = new Handler(Looper.getMainLooper());
Back to PSVM
Let’s look at ActivityThread.main() again. Here is what it is essentially doing:
public class ActivityThread {
public static void main(String... args) {
Looper.prepare(); // You can now retrieve the main looper at any time by calling Looper.getMainLooper().
Looper.setMainLooper(Looper.myLooper()); // Post the first messages to the looper.
// { ... } Looper.loop();
}
}
Now you know why this thread is called the main thread :) .
Note: As you would expect, one of the first things that the main thread will do is create the Application and call Application.onCreate().
In the next part, we will look at the relation between the Android lifecycle and the main thread, and how this can lead to subtle bugs.