Levi Albuquerque
Posted on June 3, 2018
A couple of days ago I saw a question o StackOverflow asking: "Where is the main method in Android?". I thought to myself, that's a very interesting question and I think I'll write something about it alongside with other threading stuff. Yeah, the fact that we don't see the main()
method in Android has everything to do with threading.
Where are you, main?
Let's start with the question above. Where's the main method in Android?
Well, as any java application, the entry point is the main method and, even tough we don't see it, every app starts off from a main() method just like other java apps. The class that holds it is the ActivityThread:
The ActivityThread is responsible for:
- Preparing the Main Looper - That is the Looper (more on that later) where the MainThread will be run.
- Designating a special Handler to this Main Looper
- Starts the Main Looper (by calling its loop() method).
- A whole lot of other stuff...
This is basically saying that the ActivityThread
will designate a class to execute the many methods necessary to render the UI of your app and keep it running.
And now you may be asking yourselves: Ok Levi, but you didn't mention how my main activity get's called. You just talked about a thread that is responsible by managing UI stuff
And I'll tell you: Calm down, we'll get to that
Inside the ActivityThread.java
file (it's a huge file) there is another class called simply H
that extends the Handler class and it was used before when we designated a special Handler to the Main Looper. This class is used extensively throughout the ActivityThread because it's a special Handler that deals with the possible states that our (or for that matter any other app in Android) can be.
Every handler has a nice little method called handleMessage
and this special handle's method looks like this:
public void handleMessage(Message msg) {
if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + msg.what);
switch (msg.what) {
case LAUNCH_ACTIVITY: {
ActivityClientRecord r = (ActivityClientRecord)msg.obj;
r.packageInfo = getPackageInfoNoCheck(
r.activityInfo.applicationInfo, r.compatInfo);
handleLaunchActivity(r, null);
} break;
....
This is just a small portion of it of course, it's a huge switch handling every different state. But in this portion we can see it handling the LAUNCH_ACTIVITY
case. In there it calls the method handleLaunchActivity()
which in turn calls the performLaunchActivity()
method.
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
Activity a = performLaunchActivity(r, customIntent);
...
}
The latter is very important because it adds a bunch of known structures we're used to simply use in Android such as Application, Context and Intent. It also calls the mInstrumentation.callActivityOnCreate()
that will, guess what, call our darling onCreate()
method in our main Activity.
So, it's a long path from main()
to onCreate()
to sum it up:
- ActivityThread's main gets executed
- The UI Thread is set up with its Looper, special Handler and etc.
- The Special Handler initiates the process of creating the Activity object
- The Activity object is created with associated properties
- The onCreate method is called.
Magic words: Handler, Message, MessageQueue, Looper
Now, we've talked about how we get from main()
to onCreate()
and we've used some pretty words like Handler and Looper and when you're developing an app you rarely see these in code (maybe a Handler here and there, but a Looper is quite rare, unless you're dealing with dark stuff like low level multi-threading). We see these quite frequently though when our app crashes:
That happens because they are the foundation of how android performs multi-threading. Let's start with some definitions:
Looper
A Looper is a low-level class responsible for executing a message loop for a thread. If you take a look at the implementation of the loop() method in this class, this becomes quite clear:
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
}
It's an infinite loop that reads messages from a MessageQueue and execute them. Funny thing is when I was in college we used to meddle with threads by putting them in an infinite loop. I guess that was useful now, right? Also, we loop through a thread because there is an overhead associated with creating Threads in java which can be diminished if we reuse some of the threads. Threads are meant to run once and be gone, but with a loop we can keep them around for more time.
MessageQueue
A MessageQueue is simply that, a queue that gives messages. Not much else to say.
Message
A Message is a special wrapper class that wraps some data that can be exchanged between threads.
Runnable
A Runnable in simple terms is a wrapper for a piece of work you wish to perform (a task, for lack of a better word).
Handler
A Handler processes messages (or Runnables) and schedule their processing to a Looper, that is, it receives messages and it also add messages to/from the MessageQueue associated with it. A handler is associated with a thread only and its MessageQueue, but a thread can have (and it usually does) multiple handlers.
handler.sendMessage(msg); //adds to the MessageQueue
handler.post(new Runnable() {
@Override
public void run() {
// this will run in the MQ thread
}
});
public void handleMessage(Message msg) {
// process messages from the MQ
}
So, how do they connect together?
A Handler dealing with a Message or a Runnable (units of work in Android) adds one of them to a MessageQueue that is associated with a Looper and a Thread. The Looper keeps the Thread alive looping through them and asking a Handler to execute the messages it pops from the MessageQueue.
We usually don't create these classes by ourselves but rather use a HandlerThread that creates a Looper with a Thread automatically for us.
This is just an intro to a much more deeper and complex subject (not only in Android) but I hope it was useful to someone out there :)
Posted on June 3, 2018
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.