Handler source code analysis----Why does Handler not block the thread, what is the relationship between MessageQueue, Handler and Looper?

Handler is a mechanism introduced in Android that allows developers to participate in the message loop in the processing thread. We deal with Message the most when using Handler. Message is a related class exposed by the Handler mechanism to developers. Most of the functions of Handler can be completed through the Message class. The internal implementation of Handler mainly involves the following classes: Thread, MessageQueue and Looper. As shown below:

Handlers allow us to send and process Messages, and can also associate Runnable objects with a thread's MessageQueue; each Handler instance is associated with a thread and the thread's message queue. When we create a new Handler, this Handler is bound to the thread where it was created and the thread's message queue, it transfers the Message to the message queue, and executes the message dequeued from the MessageQueue. As shown below



The following is the source code analysis:

Handler 
public Handler() {
    this(null, false);
}
public Handler(Callback callback) {
    this(callback, false);
}
public Handler(Looper looper) {
    this(looper, null, false);
}
public Handler(Looper looper, Callback callback) {
    this(looper, callback, false);
}
public Handler(boolean async) {
    this(null, async);
}
public Handler(Callback callback, boolean async) {
    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: " +
                class.getCanonicalName ());
        }
    }
//Handler associates the Looper object of the current thread here
mLooper = Looper.myLooper();
  ...
mQueue = mLooper.mQueue; mCallback = callback; mAsynchronous = async;
}
public Handler(Looper looper, Callback callback, boolean async) {
    mLooper = looper; mQueue = looper.mQueue; mCallback = callback; mAsynchronous = async;
}

First of all, look at the overloaded structures of the Handler structure, and there is a boolean parameter in them, so what is it? The official website explains that this parameter determines that the asynchronous Handler will be called and send a message, and Looper.myLoop() is executed;

PS: Message.setAsynchronous(boolean) is this value, the construction mentioned here is the construction without passing the Looper parameter

Read on:

/**
 * Handle system messages here.
 */
public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg); //If there is a message, execute the message callback
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg); //If there is no message, execute handleMessage(msg) in Handler.CallBack
    }
}
private static void handleCallback(Message message) {
    message.callback.run();
}
/**
 * Subclasses must implement this to receive messages.
 */
public void handleMessage(Message msg) {
}

And how does our message initialize it and how does it tell where the message is sent? keep looking down

//Only one obtainMessage() is posted here
public final Message obtainMessage(int what, Object obj)
{
    return Message.obtain(this, what, obj);
}

PS: The Message.obtain() function is used to prepare the message, and the reason why our Handler can tell which Activity sent the message

It is identified by the two parameters what and obj .

There are several ways to send messages: post() and postXXXX() and send() and sendXXXX(),

So is there any difference between them?

public final boolean post(Runnable r)
{
   return  sendMessageDelayed(getPostMessage(r), 0);
}

public final boolean postAtTime(Runnable r, long uptimeMillis)
{
    return sendMessageAtTime(getPostMessage(r), uptimeMillis);
}

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

public final boolean postAtFrontOfQueue(Runnable r)
{
    return sendMessageAtFrontOfQueue(getPostMessage(r));
}
public final boolean sendEmptyMessage(int what)
{
    return sendEmptyMessageDelayed(what, 0);
}

public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
    Message msg = Message.obtain();
    msg.what = what;
    return sendMessageDelayed(msg, delayMillis);
}

public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
    Message msg = Message.obtain();
    msg.what = what;
    return sendMessageAtTime(msg, uptimeMillis);
}

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;
    }
    return enqueueMessage(queue, msg, uptimeMillis);
}
public final boolean sendMessageAtFrontOfQueue(Message msg) {
    MessageQueue queue = mQueue;
    if (queue == null) {
        RuntimeException e = new RuntimeException(
            this + " sendMessageAtTime() called with no mQueue");
        Log.w("Looper", e.getMessage(), e);
//Only one obtainMessage() is posted here
public final Message obtainMessage(int what, Object obj)
{
    return Message.obtain(this, what, obj);
}

return false; } return enqueueMessage(queue, msg, 0);}
 
 
public final boolean post(Runnable r)
{
   return  sendMessageDelayed(getPostMessage(r), 0);
}

public final boolean postAtTime(Runnable r, long uptimeMillis)
{
    return sendMessageAtTime(getPostMessage(r), uptimeMillis);
}

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

public final boolean postAtFrontOfQueue(Runnable r)
{
    return sendMessageAtFrontOfQueue(getPostMessage(r));
}
public final boolean sendMessage(Message msg)
{
    return sendMessageDelayed(msg, 0);
}
public final boolean sendEmptyMessage(int what)
{
    return sendEmptyMessageDelayed(what, 0);
}

public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
    Message msg = Message.obtain();
    msg.what = what;
    return sendMessageDelayed(msg, delayMillis);
}
public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
    Message msg = Message.obtain();
    msg.what = what;
    return sendMessageAtTime(msg, uptimeMillis);
}

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;
    }
    return enqueueMessage(queue, msg, uptimeMillis);
}

public final boolean sendMessageAtFrontOfQueue(Message msg) {
    MessageQueue queue = mQueue;
    if (queue == null) {
        RuntimeException e = new RuntimeException(
            this + " sendMessageAtTime() called with no mQueue");
        Log.w("Looper", e.getMessage(), e);
        return false;
    }
    return enqueueMessage(queue, msg, 0);
}

post()及postXXXX() -----> sendMessage() ------> sendMessageDelayed() --------> sendMessageAtTime()

------>enqueueMessage()  

What the hell is enqueueMessage()? ? Then look down

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true); //Is this function familiar? ? ? This goes back to the boolean of the Handler at the beginning
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

At this point, the Handler reference is assigned to Message.target, so that each message has a reference to this Handler

The asynchrony enabled by the Boolean parameter initialized by the Handler is enabled here. Start the Handler and then return queue.enqueueMessage(msg, uptimeMillis). At this point, the message enters the queue and the Handler class task is completed. Let's start talking about MessageQueue

MessageQueue

Continuing the above, let's jump directly to the enqueueMessage(msg, uptimeMillis) function to see what he has done?

boolean enqueueMessage(Message msg, long when) {
    ...
    synchronized (this) {
        if (mQuitting) {
            ...
            msg.recycle();
            return false;
        }
        msg.markInUse();
        msg.when = when;
        Message p = mMessages;
        boolean needWake;
        if (p == null || when == 0 || when < p.when) {
            // wake up if the queue is blocked
            msg.next = p;
            mMessages = msg;
            needWake = mBlocked;
        } else {
            // Insert into the queue without us waking up
            // up the event queue unless there is a barrier at the head of the queue
            // and the message is the earliest asynchronous message in the queue.
           // The thread is blocked, p.target is null (that is, the current message has lost the handler) or the asynchronous thread needs to keep awake
		needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                for (;;) {
                    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;
    }

The point is, how does the sending Handler ensure that the thread of sending delayed messages is not blocked?

The first thing to judge is whether the message has exited. If it wants to exit, it will recycle the message, then get the message in use and assign the passed time (ie delay time) to msg.when. And what is p? In fact, p = mMessages comes in for the first time p is null, and then judges whether the message is null, whether the when of the current message is 0 or less than p. When this condition is met, p is the next message to be executed (msg.next), Then assign a value to needWake to wake up the message at all times. If the above conditions are not met, that is, the thread is blocked and an infinite loop is inserted into the queue, and the message with when = 0 or when < p.when (ie, the message with small delay) is executed first.

At this point, the entire message queue is over. The following is to talk about how Looper works

Looper

What is Looper? It is a poller designed by Android to poll for messages. It is related to the message through the handler.

private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}

The Looper structure is private and cannot be instantiated by new, and the MessageQueue and the current thread are instantiated in the structure, and the MessageQueue held in the Handler is also a reference created here. It can be seen from the structure of the handler that mQueue = mLooper.mQueue; keep looking down

public static void prepare() {
    prepare(true);
}

private static void prepare(boolean quitAllowed) {
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    sThreadLocal.set(new Looper(quitAllowed));
}
Looper instantiation is done through the Looper.prepare() method.

And our Looper 's work starts when the Handler is initialized, that is, the constructor calls Looper.myLoop();

PS: The handler construction parameter Looper is not considered here. In fact, the construction of the parameter Looper to manually initialize the Looper is the same


public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}
  // sThreadLocal.get() will return null unless you've called prepare().
  // The explanation here is not to call Looper.prepare() sThreadLocal.get() is null
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

Look at the initialization that the code says? ? Why is sThreadLocal not seeing Looper initialization? Don't worry, let's see what sThreadLocal does.

public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null)
            return (T)e.value;
    }
    return setInitialValue();
}

ThreadLocal.get() finally gets the current thread. Who is the current thread? Where did it come from? What about instantiation? How can I find that Looper.prepare() is actually in the mian() of the ActivityThread class

public static final void main(String[] args) {
        ...
        Looper.prepareMainLooper(); //That is, the Looper has been opened here    
        if (sMainThreadHandler == null) {
            sMainThreadHandler = new Handler();
        }
        ActivityThread thread = new ActivityThread();
        thread.attach(false);
        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }
        Looper.loop();
        ...
    }
public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}

Fuck!! How can this happen? ? When the Handler is initialized, the Loop starts to work. In fact, we all know that ActivityThread is the entry point of the Android program. In the main() of ActivityThread, we have not seen other things that do message processing, that is to say, Handler, Looper When the program starts, it has already started to work, that is to say, our own ThreadLocal.get() obtains the ActivityThread thread, which is the main thread, and finds the place where Looper is initialized. Let's start polling

public static void loop() {
        final Looper me = myLooper();
        if (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 the local process,
        // and keep track of what that identity token actually is.
	// Make sure this thread is local and log this thread
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();

        for (;;) {
            Message msg = queue.next(); // might block might block
            if (msg == null) {
                // Exit without a message.
                return;
            }

            // This must be in a local variable, in case a UI event sets the logger
	    // Must be in a local variable, in case the UI event sets the logger
            final Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;

            final long traceTag = me.mTraceTag;
            if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
            }
            final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            final long end;
            try {
                msg.target.dispatchMessage(msg); //Dispatch message
                end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            if (slowDispatchThresholdMs > 0) {
                final long time = end - start;
                if (time > slowDispatchThresholdMs) {
                    Slog.w(TAG, "Dispatch took " + time + "ms on "
                            + Thread.currentThread().getName() + ", h=" +
                            msg.target + " cb=" + msg.callback + " msg=" + msg.what);
                }
            }

            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }

            // make sure the dispatch thread
            // Thread representation is not corrupted
            final long newIdent = Binder.clearCallingIdentity();
            if (ident != newIdent) {
                Log.wtf(TAG, "Thread identity changed from 0x"
                        + Long.toHexString(ident) + " to 0x"
                        + Long.toHexString(newIdent) + " while dispatching to "
                        + msg.target.getClass().getName() + " "
                        + msg.callback + " what=" + msg.what);
            }

            msg.recycleUnchecked();
        }
    }

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325406132&siteId=291194637