Monday 26 January 2015

Looper




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