Loopers, Handlers, RuntimeExceptions explained…
What’s a Looper, what’s a Handler and what’s up with “Can’t create handler inside thread that has not called Looper.prepare()?!”
NOTE: as always, consider everything I say as prefaced with “my understanding is that…”
A Looper is a simple message loop for a thread. Once loop() is called, an unending loop (literally while (true)) waits for messages to appear in a MessageQueue and then processes it once one shows up.
Some basic facts: a Looper is associated with a particular thread. When Looper.prepare() is called, it simply checks to see if the calling Thread already has a Looper associated with it. If it does, it throws a RuntimeException because you can only have one Looper per thread.
Now, many have experienced issues when using the parameterless constructor to create a Handler. Let’s back up. What’s a Handler? Handler is an object that simply lets you send/process messages and runnables associated with a thread’s MessageQueue. Internally, the MessageQueue that a Handler uses in order to do this is the MessageQueue that the Thread’s Looper “waits” for messages in. In other words, a Handler is the interface to the MessageQueue that the Looper is constantly “scanning”. Now you can understand the RuntimeException of “Can’t create handler inside thread that has not called Looper.prepare()”. This happens because the Handler can’t find a Looper associated with the Thread in which you’re trying to create the Handler (and therefore has no queue for it to use). It’s like you’re telling the system that you want to start pumping messages into a non-existent Queue. Calling Looper.prepare is what initially associates a new Looper (and the MessageQueue) with the “current thread”.
So, you can of course use the Handler constructor that takes a Looper (new Handler(Looper looper) if you already have a reference to one. And, you can always get a Looper by calling the static method Looper.getMainLooper(), which “Returns the application’s main looper, which lives in the main thread of the application.”
How does this work? Seems like magic, but the reason it works is because theres a static method inside of Looper called prepareMainLooper which the system calls inside of main(). Yes, the main which developers never encounter. main() is in the class ActivityThread and, sure enough, it calls Looper.prepareMainLooper. This is how/why you can do something Handler handler = new Handler(Looper.getMainLooper()). It’s because the system already created a “main” looper for you.
Feedback I’m interested in:
- “You were wrong about X”
- “You could be clearer about Y”
- “Here are some other helpful resources for learning about Loopers/Threads/Handler internals”