1. An overview of the

Handlers allow you to send and process messages, as well as Runnable objects associated with threads. Each Handler instance is associated with a thread and its MessageQueue. Even when you create a Handler, that Handler must bind to a thread and its message queue, and once it’s created, it can pass messages and runnables to the Message Queue and execute them as they exit the Message queue.

Handler has two main uses:

  • Schedule the execution of messages and runnables at some point in the future
  • Enqueue operations that will be performed on different threads

When your application is created, the main thread runs a message Queue to manage the top-level application objects (activities, Broadcast Receivers, etc.) and any other Windows they create. You can create your own thread and use a Handler to connect to the main thread

2. Source code analysis

2.1 MessageQueue- MessageQueue

MessageQueue is a low-level class that distributes the list of messages it holds through Looper. Messages are not added directly to MessageQueue, but through the Handler object associated with Looper.

  • Message – a Message

This class implements the Parcelable interface, which you can think of as a data class

**
 *
 * Defines a message containing a description and arbitrary data object that can be
 * sent to a {@link Handler}.  This object contains two extra int fields and an
 * extra object field that allow you to not do allocations inMany cases. * Define messages containing descriptions and arbitrary data objects * to be sent to Handler. This object contains two extra int fields and one * extra object field, allowing you to go unallocated in many cases. * <p class="note">While the constructor of Message is public, the best way to get
 * one of these is to call {@link #obtain Message.obtain()} or one of the
 * {@link Handler#obtainMessage Handler.obtainMessage()} methods, which will pull* Them from a pool of recycled objects. Instead of using New Message() directly to create a Message object, the best way to do this is through the handler.obtainMessage () * method, Public final class Message implements Parcelable {/** @hide */ public static final Object sPoolSync  = new Object(); /** * Return a new Message instance from the global pool. Allows us to * avoid allocating new objectsinMany cases. * Returns a new message instance from the global pool. Let's avoid assigning new objects in many cases. */ public static Messageobtain() {
        synchronized (sPoolSync) {
            if(sPool ! = null) { Message m = sPool; sPool = m.next; m.next = null; m.flags = 0; // clearin-use flag
                sPoolSize--;
                returnm; }}return new Message();
    }
    **
     * Same as {@link #obtain()}, but sets the value for the target member on the Message returned.* Same as obtain(), but sets the value of the target member on the returned message. * @returnPublic static Message obtain(Handler h) {Message m = obtain();  m.target = h; // Receive the Handler that is passedreturnm; }}Copy the code

2.2 Looper- Message poller

Looper is used to poll messages for threads. The thread has no Lopper associated with it by default and must be created using the looper.prepare () method. Example:

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(); }}Copy the code
  • ThreadLocal provides thread-local variables through which to access copies of thread-independent initialization variables. That is, Looper interacts with the current thread through the ThreadLocal class
Private Looper(Boolean quitAllowed) {mQueue = new MessageQueue(quitAllowed); mQueue = new MessageQueue(quitAllowed); // instantiate message queue mThread = thread.currentThread (); } ** Initialize the current thread as a looper. * This gives you a chance to create handlers thatthen 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()}.* Initialize the current thread's Looper * give you a moment to create a Handler and reference it before it's ready to poll * Remember looper.loop () after calling this method, Quit () to exit */ public static voidprepare() {
    prepare(true);
}

// sThreadLocal.get() will returnNull unless you have called prepare(). // The looper.prepare () method must be called first, Static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); Private static void prepare(Boolean quitAllowed) {// Each thread can have only one looperif(sThreadLocal.get() ! = null) {throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(quitAllowed)); Return the Looper object associated with the current thread. Returns * nullifThe calling thread is not associated with a Looper. * Return Looper */ public static @nullable LoopermyLooper() {
    return sThreadLocal.get();
}

/**
 * Run the message queue in this thread. Be sure to call
 * {@link #quit()} to end the loop.Public static void to run messages in the current threadloop() { final Looper me = myLooper(); / / for starsif(me == null) {// Throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } final MessageQueue queue = me.mQueue; // Make sure the identity of this thread is that of thelocalProcess, // and keep track of what that identity token actually is. Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity(); // Allow overriding a threshold with a system prop. e.g. // adb shell'setprop log.looper.1000.main.slow 1 && stop && start'
    final int thresholdOverride =
            SystemProperties.getInt("log.looper."
                    + Process.myUid() + "."
                    + Thread.currentThread().getName()
                    + ".slow", 0);

    boolean slowDeliveryDetected = false;

    for(;;) {Message MSG = queue.next(); // might blockif(MSG == null) {// No message indicates that the message queue is the formspapers.return; } *** final Long dispatchStart = needStartTime? SystemClock.uptimeMillis() : 0; final long dispatchEnd; try { msg.target.dispatchMessage(msg); // Call Handler dispatchMessage(MSG) dispatchEnd = needEndTime? SystemClock.uptimeMillis() : 0; } finally {if(traceTag ! = 0) { Trace.traceEnd(traceTag); }} // Reclaim messages that might be in use. MessageQueue and Looper are used internally when processing message queues. msg.recycleUnchecked(); } } //sThreadLocal.get() public Tget() { Thread t = Thread.currentThread(); ThreadLocalMap is a custom HashMap() that is only suitable for maintaining thread-local values. Each Thread holds a ThreadLocalMap object ThreadLocalMap = getMap(t); // Pass the current thread to get the ThreadLocalMap object of the current thread -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --, ThreadLocalMap getMap(Thread t) {returnt.threadLocals; } -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -if(map ! = null) { ThreadLocalMap.Entry e = map.getEntry(this);if(e ! = null) { @SuppressWarnings("unchecked")
            T result = (T)e.value;
            returnresult; }}return setInitialValue();
}
Copy the code

2.3 Handler

2.3.1 Creating a Handler Object

/** * Callback interface you can use when instantiating a Handler to avoid * having to implement your own subclass of Handler. * If you don't want to implement this by inheriting Handler, Public interface Callback {/** * @param MSG A {@link android.os.Message Message} object * @return True ifno further handling is desired */ public boolean handleMessage(Message msg); } // Default constructor // If the thread to which this Handler is bound does not have looper, it will receive no messages and will throw the public exceptionHandler() {
    this(null, false); } // The default constructor Callback is empty, so when we create an instance with new Handler(), we must override Handler's handleMessage(Message MSG) method, i.e. Private class MyHandler: private class MyHandler: private class MyHandler:Handler(){ override fun handleMessage(msg: Message?) { super.handleMessage(msg) } } } -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - / / take the initiative to implement the Callback interface, Public Handler(Callback Callback) {this(Callback,false); } companion object { private val mHandler1:Handler = Handler(object :Handler.Callback{ override fun handleMessage(msg: Message?) : Boolean {return true
        }
    })
    或
    private val mHandler2:Handler = Handler(Handler.Callback {
        true})} -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - / * * * Use the provided  {@link Looper} instead of the default one. * * @param looper The looper, Public Handler(looper looper) {this(looper, null,false); } // I have seen that some predecessors create Handler instances by passing the main thread looper, but this is not necessary, Handler defaults to the main thread looper(which will be explained below). Use your own supplied looper instead of the default private val mHandler:Handler = object :Handler(looper.getMainLooper ()){Override Fun handleMessage(msg: Message?) { super.handleMessage(msg) } } -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - / / same as above, the structure If you don't want to override handleMessage(MSG: Message?) Public Handler(Looper Looper, Callback Callback) {this(Looper, Callback, Callback);false); } public Handler(Boolean async) {this(null, async); Public Handler(looper looper, Callback Callback, Boolean async) {mLooper = looper; mQueue = looper.mQueue; mCallback = callback; mAsynchronous = async; } -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - / * * Set this flag totrue to detect anonymous, localor member classes * that extend this Handler class and that are not static. These kind * of classes can potentially Create Leaks. * Sets this flag totrueTo detect that anonymous, local, or member classes * inherit handlers that are not static and may have memory leaks */ private static final Boolean FIND_POTENTIAL_LEAKS =false; Handler(), Handler(Callback Callback), Handler(Boolean async), Handler(Boolean async) boolean async) {if(FIND_POTENTIAL_LEAKS) {// Checks for possible memory leaks, default isfalse
        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(); / / for starsif(mLooper == null) {// Throw new RuntimeException("Can't create handler inside thread " + Thread.currentThread()
                    + " that has not called Looper.prepare()"); } mQueue = mLooper.mQueue; // Get the message queue in Looper mCallback = callback; mAsynchronous = async; } private static void handleCallback(Message message) { message.callback.run(); /** * Handle system messages here. */ public void dispatchMessage(Message MSG) {if(msg.callback ! = null) {// If Message Runnable is not null handleCallback(MSG); Call Handler's handleMessage(MSG) method to handleMessage}else {
        if(mCallback ! = null) {// If Handler's Callback is not nullif(McAllback.handlemessage (MSG)) {// Call the handleMessage(MSG) method of Callbackreturn; }} // Otherwise call handleMessage(MSG); }}Copy the code

2.3.2 Creating a Message Object

public final Message obtainMessage()
{
    return Message.obtain(this);
}
public final Message obtainMessage(int what)
{
    return Message.obtain(this, what);
}
public final Message obtainMessage(int what, Object obj)
{
    return Message.obtain(this, what, obj);
}
public final Message obtainMessage(int what, int arg1, int arg2)
{
    return Message.obtain(this, what, arg1, arg2);
}
public final Message obtainMessage(int what, int arg1, int arg2, Object obj)
{
    return Message.obtain(this, what, arg1, arg2, obj);
}
Copy the code

2.3.3 send Message

/**
 * Causes the Runnable r to be added to the message queue.
 * The runnable will be run on the thread to whichThis handler is * attached. * Adds Runnable to the message queue. * Runnable will run on the same thread as this Handler. * * @return Returns true if the Runnable was successfully placed in to the 
 *         message queue.  Returns falseOn failure, usually because the * looper processing the message queue is no longer intended. * Runnable successful addition to a message queue returns true, failure returnsfalse
 */
public final boolean post(Runnable r)
{
   returnsendMessageDelayed(getPostMessage(r), 0); } private static Message getPostMessage(Runnable r) { Message m = Message.obtain(); Create Message object m.callback = r; Pass Runnale to Messagereturn m;
}
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
    ifDelayMillis = 0; delayMillis = 0; delayMillis = 0; }returnsendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); } public final Boolean postDelayed(Runnable r, long delayMillis) {public final Boolean postDelayed(Runnable r, long delayMillis) {return sendMessageDelayed(getPostMessage(r), delayMillis);
}

public final boolean postDelayed(Runnable r, Object token, long delayMillis)
{
    return sendMessageDelayed(getPostMessage(r, token), delayMillis);
}

public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

 public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
    MessageQueue queue = mQueue;
    if (queue == null) {
        RuntimeException e = new RuntimeException(
                this + " sendMessageAtTime() called with no mQueue");
        Log.w("Looper", e.getMessage(), e);
        return false;
    }
    returnenqueueMessage(queue, msg, uptimeMillis); } private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; // Pass the current Handler to Message's Handlerif(mAsynchronous) {// Whether MSG. SetAsynchronous (true); } // Finally call the enqueueMessage method of MessageQueuereturn queue.enqueueMessage(msg, uptimeMillis);
}
MessageQueue:
boolean enqueueMessage(Message msg, long when) {
    if(msg.target == null) {//Message Handler is null, throw new IllegalArgumentException("Message must have a target.");
    }
    if(MSG. IsInUse ()) {//Message is being used, throw new IllegalStateException(MSG +)" This message is already in use.");
    }

    synchronized (this) {
        if(McOntract) {// Quit IllegalStateException e = new IllegalStateException(msg.target +)" sending message to a Handler on a dead thread"); Log.w(TAG, e.getMessage(), e); msg.recycle(); / / retrieve the Messagereturn false; } msg.markInUse(); Set Flag msg.when = when; Message p = mMessages; boolean needWake; // Whether to wake upif (p == null || when == 0 || when < p.when) {
            // New head, wake up the event queue if blocked.
            msg.next = p;
            mMessages = msg;
            needWake = mBlocked;
        } else {
            // Inserted within the middle of the queue.  Usually we don't have to wake // up the event queue unless there is a barrier at the head of the queue // and the message is the Echo asynchronous message in the queue. Normally we do not need to wake up // start the message queue unless the queue header is blocked // the message is the earliest asynchronous message in the queue. needWake = mBlocked && p.target == null && msg.isAsynchronous(); Message prev; for (;;) {// enable unlimited polling prev = p; p = p.next; if (p == null || when < p.when) { break; } if (needWake && p.isAsynchronous()) { needWake = false; } } msg.next = p; // invariant: p == prev.next prev.next = msg; } // We can assume mPtr ! = 0 because mQuitting is false. if (needWake) { nativeWake(mPtr); } } return true; }Copy the code

3. Summary