Handling multiple Messages Inside a Thread in Android
In
general , Thread doesn't have a MessageQueue which helps us to
process the multiple messages by a Thread.To do such thing , we need
Looper.
Two
important things we need to understand before we accomplish handling
multiple messages inside a thread are
1.Handler
2.Looper
1.Handler
According
to Android documentation ,
A
Handler allows you to send and process
Message
and Runnable objects associated with a thread's MessageQueue
.
Each Handler instance is associated with a single thread and that
thread's message queue. When you create a new Handler, it is bound to
the thread / message queue of the thread that is creating it -- from
that point on, it will deliver messages and runnables to that message
queue and execute them as they come out of the message queue.
There
are two main uses for a Handler:
(1)
to schedule messages and runnables to be executed as some point in
the future; and
(2)
to enqueue an action to be performed on a different thread than your
own.
2.Looper
According
to Android documentation ,
Class
used to run a message loop for a thread. Threads by default do not
have a message loop associated with them; to create one, call
prepare() in the thread that is to run the loop, and then
loop()
to have it process messages until the loop is stopped.
The
following is the example of Looper
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();
}
}
In the above example, Looper.prepare() is called and the Handler process the
messages . The prepare() inside the Looper class methos deos the following ,
public static final void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper());
}
The Looper constructor are as follows
private Looper() {
mQueue = new MessageQueue();
mRun = true;
mThread = Thread.currentThread();
}
If you take a look at above methods , the following things can br understood, 1.prepare method is making a looper object and set it to a Threaadlocal object. 2.Inside the looper constructor , it creates a MessageQueue and assigns the
current thread to a local thread .
So now If we look at Handler's Constructor
public Handler() { if (FIND_POTENTIAL_LEAKS) { final Class<? extends Handler> klass = getClass(); if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) && (klass.getModifiers() & Modifier.STATIC) == 0) { Log.w(TAG, "The following Handler class should be static or leaks might occur: " + klass.getCanonicalName()); } } mLooper = Looper.myLooper(); if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } mQueue = mLooper.mQueue; mCallback = null; } public static final Looper myLooper() { return (Looper)sThreadLocal.get(); }
The Handler calls the Looper's myLooper() method which returns the looper retrieved from ThreadLocal object. The Looper's loop() methodis as follows public static final void loop() { Looper me = myLooper(); MessageQueue queue = me.mQueue; while (true) { Message msg = queue.next(); // might block //if (!me.mRun) { // break; //} if (msg != null) { if (msg.target == null) { // No target is a magic identifier for the quit message. return; } if (me.mLogging!= null) me.mLogging.println( ">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what ); msg.target.dispatchMessage(msg); if (me.mLogging!= null) me.mLogging.println( "<<<<< Finished to " + msg.target + " " + msg.callback); msg.recycle(); } } } The above method loops through the MessageQueue and preocess the each
message.All the above methods were taken from the respective classes from
Android Framework.
Now in our example , we created two threads one with looper and one without looper. The Thread without looper ia as follows
class ThreadWithoutLooper extends Thread{ @Override public void run() { super.run(); mHandler = new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what){ case 1: Log.d(DEBUG_TAG, "NO LOOPER ATTACHED 1 "+msg.what); break; case 2: Log.d(DEBUG_TAG, "NO LOOPER ATTACHED 2 "+msg.what); break; } } }; } } You will get the following error java.lang.RuntimeException: Can't create handler inside thread that has not
called Looper.prepare().Now take a look in the above handler constructor , if the
looper is null this error will be thrown.
Comment the line 28 and 29 , now run the app you will get the following in the log
class ThreadWithtLooper extends Thread{ @Override public void run() { super.run(); Looper.prepare(); mHandler = new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what){ case 1: Log.d(DEBUG_TAG, "LOOPER ATTACHED 1 "+msg.what); break; case 2: Log.d(DEBUG_TAG, "LOOPER ATTACHED 2 "+msg.what); break; } } }; MessageQueue messageQueue = Looper.myQueue(); Log.d(DEBUG_TAG , ""+messageQueue); Looper.loop(); } }
com.vp.threadqueue D/MainActivity﹕ LOOPER ATTACHED 2 2 The Source code can be found in https://github.com/falgee/Threadqueue
We hope you understand how to process multiple messages in a thread
can be accomplished by looper i n the Android Framework. Please feel free to comment or posta questions