SoFunction
Updated on 2025-03-01

Detailed explanation of Android message processing mechanism Looper and Handler

Message:information,其中包含了informationID,informationdeal with对象以及deal with的数据wait,Depend onMessageQueueUnified lineup,终Depend onHandlerdeal with。
Handler:deal with者,ResponsibleMessage的发送及deal with。useHandlerhour,Need to be implementedhandleMessage(Message msg)Methods to specificMessage进行deal with,For example, updateUIwait。
MessageQueue:information队列,Used to storeHandler发送过来的information,And followFIFORule execution。certainly,StoreMessageNot a preservation of practical significance,InsteadMessageConnected in a linked list,wait待LooperThe draw。
Looper:information泵,Continuously fromMessageQueueDrawMessageimplement。therefore,oneMessageQueue需要oneLooper。
Thread:Thread,Responsible调度整个information循环,即information循环的implement场所。

The message queue and message loop of the Android system are both for specific threads. A thread can exist (or of course it can not exist) a message queue and a message loop (Looper). Messages from specific threads can only be distributed to this thread and cannot be communicated across threads or across processes. However, the created worker thread has no message loop and message queue by default. If you want the thread to have a message queue and message loop, you need to call () in the thread to create the message queue first, and then call () to enter the message loop. As shown in the following example:

 LooperThread Thread {
    Handler mHandler;

    run() {
     ();

     mHandler = Handler() {
        handleMessage(Message msg) {
         
       }
     };

     ();
   }
 }

//Looper class analysis
//I haven't found a suitable way to analyze the code, so I can only do this. Each important line will be commented on it
//The functional code will be added to the code with a piece of analysis

 public class Looper {
  //static variable, determine whether to print debug information.   private static final boolean DEBUG = false;
   private static final boolean localLOGV = DEBUG ?  : ;
 
   // () will return null unless you've called prepare().
 //What does the thread local storage function encapsulation, TLS, thread local storage, mean?  Because storage is either on the stack, for example internal variables defined within a function.  Either on the heap, such as new or malloc stuff //But nowadays systems such as Linux and Windows provide thread-local storage space, that is, this storage space is related to threads, and there is an internal storage space in a thread. In this way, I will store thread-related things to //In the TLS of this thread, you don’t need to put it on the heap and perform synchronization operations.   private static final ThreadLocal sThreadLocal = new ThreadLocal();
 //Message queue, MessageQueue, you can tell it is a queue by looking at the name...   final MessageQueue mQueue;
   volatile boolean mRun;
 //The thread related to this looper is initialized to null   Thread mThread;
   private Printer mLogging = null;
 //static variable represents the main thread of a UI Process (or maybe service, here is the default UI)   private static Looper mMainLooper = null;
   
   /** Initialize the current thread as a looper.
    * This gives you a chance to create handlers that then reference
    * this looper, before actually starting the loop. Be sure to call
    * {@link #loop()} after calling this method, and end it by calling
    * {@link #quit()}.
    */
 //Set this Looper object in TLS. If the thread has set the Looper, it will report an error //This means that one thread can only set one looper   public static final void prepare() {
     if (() != null) {
       throw new RuntimeException("Only one Looper may be created per thread");
     }
     (new Looper());
   }
   
   /** Initialize the current thread as a looper, marking it as an application's main 
   * looper. The main looper for your application is created by the Android environment,
   * so you should never need to call this function yourself.
   * {@link #prepare()}
   */
 //The main message loop of the UI program set by the framework. Note that this main message loop will not exit actively. //  
   public static final void prepareMainLooper() {
     prepare();
     setMainLooper(myLooper());
 //Judge whether the main message loop can exit... //Send an exit application to looper through the quit function     if (()) {
       myLooper(). = false;
     }
   }
 
   private synchronized static void setMainLooper(Looper looper) {
     mMainLooper = looper;
   }
   
   /** Returns the application's main looper, which lives in the main thread of the application.
   */
   public synchronized static final Looper getMainLooper() {
     return mMainLooper;
   }
 
   /**
   * Run the message queue in this thread. Be sure to call
   * {@link #quit()} to end the loop.
   */
 //Message loop, the whole program is here while. //This is a static function!   public static final void loop() {
     Looper me = myLooper();//Fetch the corresponding looper object from this thread     MessageQueue queue = ;//Get message queue object...     while (true) {
       Message msg = (); // might block get a pending message in the message queue..       //if (!) {//Does it need to exit?  mRun is a volatile variable, which is synchronized across threads, so there should be a place to set it.       //  break;
       //}
       if (msg != null) {
         if ( == null) {
           // No target is a magic identifier for the quit message.
           return;
         }
         if (!= null) (
             ">>>>> Dispatching to " +  + " "
             +  + ": " + 
             );
         (msg);
         if (!= null) (
             "<<<<< Finished to  " +  + " "
             + );
         ();
       }
     }
   }
 
   /**
   * Return the Looper object associated with the current thread. Returns
   * null if the calling thread is not associated with a Looper.
  */
//Return the thread-related looper public static final Looper myLooper() {
   return (Looper)();
 }

 /**
  * Control logging of messages as they are processed by this Looper. If
  * enabled, a log message will be written to <var>printer</var> 
  * at the beginning and ending of each message dispatch, identifying the
  * target Handler and message contents.
  * 
  * @param printer A Printer object that will receive log messages, or
  * null to disable message logging.
  */
//Set the debug output object. When looper loops, relevant information will be printed. It is best to use it for debugging. public void setMessageLogging(Printer printer) {
   mLogging = printer;
 }
 
 /**
  * Return the {@link MessageQueue} object associated with the current
  * thread. This must be called from a thread running a Looper, or a
  * NullPointerException will be thrown.
  */
 public static final MessageQueue myQueue() {
   return myLooper().mQueue;
 }
//Create a new looper object,//A message queue is allocated internally and set mRun to true private Looper() {
   mQueue = new MessageQueue();
   mRun = true;
   mThread = ();
 }

 public void quit() {
   Message msg = ();
   // NOTE: By enqueueing directly into the message queue, the
   // message is left with a null target. This is how we know it is
   // a quit message.
   (msg, 0);
 }

 /**
  * Return the Thread associated with this Looper.
  */
 public Thread getThread() {
   return mThread;
 }
 //The following is simple, printing, exception definition, etc. public void dump(Printer pw, String prefix) {
   (prefix + this);
   (prefix + "mRun=" + mRun);
   (prefix + "mThread=" + mThread);
   (prefix + "mQueue=" + ((mQueue != null) ? mQueue : "(null"));
   if (mQueue != null) {
     synchronized (mQueue) {
       Message msg = ;
       int n = 0;
       while (msg != null) {
         (prefix + " Message " + n + ": " + msg);
         n++;
         msg = ;
       }
       (prefix + "(Total messages: " + n + ")");
     }
   }
 }

 public String toString() {
   return "Looper{"
     + ((this))
     + "}";
 }

 static class HandlerException extends Exception {

   HandlerException(Message message, Throwable cause) {
     super(createMessage(cause), cause);
   }

   static String createMessage(Throwable cause) {
     String causeMsg = ();
     if (causeMsg == null) {
       causeMsg = ();
     }
     return causeMsg;
   }
 }
}

So how do you send messages to this message queue? ? Call looper's static function myQueue to get the message queue, so you can insert messages into it yourself. However, this method is more troublesome, and the handler class will work at this time. Let’s take a look at the handler’s code first and you’ll understand.

 class Handler{
 ..........
 //Handler default constructor public Handler() {
 //I don't understand why this if is used for the time being. It should be related to the deep content of Java.     if (FIND_POTENTIAL_LEAKS) {
       final Class<? extends Handler> klass = getClass();
       if ((() || () || ()) &&
           (() & ) == 0) {
         (TAG, "The following Handler class should be static or leaks might occur: " +
           ());
       }
     }
 //Get the looper object of this thread //If this thread has not set looper yet, this time, throw an exception     mLooper = ();
     if (mLooper == null) {
       throw new RuntimeException(
         "Can't create handler inside thread that has not called ()");
     }
 //Shameless, I just turned the looper's queue and my own queue into one //In this case, if I add messages through the handler encapsulation mechanism, it is equivalent to directly adding them to the looper message queue.     mQueue = ;
     mCallback = null;
   }
 //There are several constructors, one with callback and the other with looper // Set the looper from the outside   public Handler(Looper looper) {
     mLooper = looper;
     mQueue = ;
     mCallback = null;
   }
 // With callback, a handler can set a callback.  If there is a callback, //All messages sent through this handler have callback processing, which is equivalent to a total centralized processing //Analysis will be made when viewing dispatchMessage later public Handler(Looper looper, Callback callback) {
     mLooper = looper;
     mQueue = ;
     mCallback = callback;
   }
 //
 //Send a message through handler //A sendMessageDelayed inside is called public final boolean sendMessage(Message msg)
   {
     return sendMessageDelayed(msg, 0);
   }
 //FT, another layer is encapsulated, this time I call sendMessageAtTime //Because the delay time is based on the current call time, it is necessary to obtain absolute time to pass to sendMessageAtTime public final boolean sendMessageDelayed(Message msg, long delayMillis)
   {
     if (delayMillis < 0) {
       delayMillis = 0;
     }
     return sendMessageAtTime(msg, () + delayMillis);
   }
 
 
 public boolean sendMessageAtTime(Message msg, long uptimeMillis)
   {
     boolean sent = false;
     MessageQueue queue = mQueue;
     if (queue != null) {
 //Set the message target to yourself and then add it to the message queue //For a data structure like queue, the operation is relatively simple        = this;
       sent = (msg, uptimeMillis);
     }
     else {
       RuntimeException e = new RuntimeException(
         this + " sendMessageAtTime() called with no mQueue");
       ("Looper", (), e);
     }
     return sent;
   }
 // Do you still remember the message loop processing in looper //After getting a message from the message queue, the dispatchMesage function of its target will be called //The target of message has been set to handler, so // Finally, it will be transferred to the handler's msg processing //There is a problem with the process here public void dispatchMessage(Message msg) {
 //If msg itself sets callback, then it will be handled directly by this callback.     if ( != null) {
       handleCallback(msg);
     } else {
 //If the handler's callback has, then it will be handed over to this callback for processing---equivalent to centralized processing      if (mCallback != null) {
         if ((msg)) {
           return;
         }
      }
 // Otherwise, it will be handed over to the derived processing, and the base class default processing will do nothing.       handleMessage(msg);
     }
   }
 ..........
 }

generate

    Message msg = ();
     = what;
    ();

send

    MessageQueue queue = mQueue;
    if (queue != null) {
       = this;
      sent = (msg, uptimeMillis);
    }

In the sendMessageAtTime(Message msg, long uptimeMillis) method, we see that it finds the MessageQueue it refers to, and then sets the Message target to itself (the purpose is to find the correct Handler in the message processing process), and then incorporates this Message into the message queue.

Draw

    Looper me = myLooper();
    MessageQueue queue = ;
    while (true) {
      Message msg = (); // might block
      if (msg != null) {
        if ( == null) {
          // No target is a magic identifier for the quit message.
          return;
        }
        (msg);
        ();
      }
    }

In the loop() function, we see that there is a dead loop here, constantly obtaining the next (next method) Message from the MessageQueue, and then passing it over to the correct Handler to handle (dispatchMessage method).

deal with

    if ( != null) {
      handleCallback(msg);
    } else {
      if (mCallback != null) {
        if ((msg)) {
          return;
        }
      }
      handleMessage(msg);
    }

In the dispatchMessage(Message msg) method, one of the branches is to call the handleMessage method to handle this Message, which is why we describe the need to implement handleMessage(Message msg) when using Handler in the responsibility department.

As for another branch in the dispatchMessage method, I will explain it in the following content.

At this point, we see that a Message is sent by Handler, the MessageQueue is enqueued, and the Looper is drawn, and it is once again returned to Handler's arms. This circle of circles also helps us turn synchronous operations into asynchronous operations.

3) For the rest of the parts, we will discuss the threads where the Handler is located and the way to update the UI.

In the main thread (UI thread), if the Looper object is not passed in when creating the Handler, the Looper object of the main thread (UI thread) will be directly used (the system has created it for us); in other threads, if the Looper object is not passed in when creating the Handler, then the Handler will not be able to receive processing messages. In this case, the general approach is:

        class LooperThread extends Thread {
                public Handler mHandler;
                public void run() {
                        ();
                        mHandler = new Handler() {
                                public void handleMessage(Message msg) {
                                       // process incoming messages here
                                }
                        };
                        ();
                }
        }

Before creating the Handler, prepare a Looper() for the thread, and then let the Looper run() and extract the Message so that the Handler can work normally.

Therefore, Handler processing messages are always run in the thread that creates the Handler. In our message processing, there are many operations to update the UI. Incorrect threads directly update the UI will throw exceptions. Therefore, you need to always care about which thread the Handler was created.

How to update the UI so as not to have exceptions? The SDK tells us that there are four ways to access UI threads from other threads:

·      (Runnable)
·      (Runnable)
·      (Runnable, long)
·      Handler
Among them, the key point is the (Runnable) method. In the post(Runnable action) method, the View obtains the Handler of the current thread (i.e., the UI thread), and then posts the action object into the Handler. In the Handler, it wraps the passed action object into a Message (the callback of the Message is an action), and then throws it into the message loop of the UI thread. When the Handler processes the Message again, there is a branch (the unexplained one) that is set for it and directly calls the runnable run method. At this time, it has been routed to the UI thread, so we can update the UI without any worries.

4) A few summary

·      The Handler process runs in the thread that creates the Handler
·       A Looper corresponds to a MessageQueue
·                                                              �
·       A Looper can correspond to multiple Handlers
·       When you are not sure about the current thread, try to call the post method when updating the UI