What is Main Thread in Android
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. } }
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().
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();
}
}
The HandlerThread class does everything for you:
HandlerThread thread = new HandlerThread("SquareHandlerThread"); thread.start(); // starts the thread. Looper looper = thread.getLooper();
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 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);
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. } });
// DON'T DO THIS
Handler handler = new Handler();
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
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(); } }
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.
No comments:
Post a Comment