In Android, inherit the JAVA main thread, child thread concept. The main thread, called ActivityThread, specifies that only this thread can manipulate the UI. The other data that we’re getting, the data that we’re getting in a network request, we’re only getting from the child thread, so we need someone to be able to move the data from the child thread to the main thread, so basically, we need someone to help the thread switch, and that’s what Handler is.

1. Main concepts of Handler

The main concepts in Handler are the following three students.

1.MessageQueue

2.Looper

3.Handler

M, as the name implies, is a message queue. According to the name, this student is responsible for storing information.

Student L, whose name is Lupo, is mainly responsible for checking and supervising whether there is any new message coming from student M, and taking out and processing the message.

H’s responsibility is just like a manager. He initializes the other two students and sends external information to M.

Responsibilities are clearly delineated and uncomplicated, just like writing code.

1.1 MessageQueue

M is not a queue. It is named like a queue, but it is actually a single linked list. That is, the messages are retrieved sequentially. Singly linked lists are great for both insertion and deletion.

Look at the source code and see how M inserted a message:

boolean enqueueMessage(Message msg, long when) {
    if (msg.target == null) {
        throw new IllegalArgumentException("Message must have a target.");
    }
    if (msg.isInUse()) {
        throw new IllegalStateException(msg + " This message is already in use.");
    }

    synchronized (this) {
        if (mQuitting) {
            IllegalStateException e = new IllegalStateException(
                    msg.target + " sending message to a Handler on a dead thread");
            Log.w(TAG, e.getMessage(), e);
            msg.recycle();
            return false;
        }

        msg.markInUse();
        msg.when = when;
        Message p = mMessages;
        boolean needWake;
        if (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 earliest asynchronous message in the queue. 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; }Copy the code

Take a look at the insertion operation of a single linked list.

Take a look at M’s next() method, which L calls to get the next message.

The next() method is a blocking method that blocks the current thread. This means that you need to recycle resources when the Handler is not in use, otherwise the thread will continue to consume resources. Now let’s look at how resources are recycled.

Next () method

Message next() {
    // Return here if the message loop has already quit and been disposed.
    // This can happen if the application tries to restart a looper after quit
    // which is not supported.
    final long ptr = mPtr;
    if (ptr == 0) {
        return null;
    }

    int pendingIdleHandlerCount = -1; // -1 only during first iteration
    int nextPollTimeoutMillis = 0;
    for (;;) {
        if(nextPollTimeoutMillis ! = 0) { Binder.flushPendingCommands(); } nativePollOnce(ptr, nextPollTimeoutMillis); synchronized (this) { // Try to retrieve the next message. Returnif found.
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages;
            if(msg ! = null && msg.target == null) { // Stalled by a barrier. Find the next asynchronous messagein the queue.
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while(msg ! = null && ! msg.isAsynchronous()); }if(msg ! = null) {if (now < msg.when) {
                    // Next message is not ready.  Set a timeout to wake up when it is ready.
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                } else {
                    // Got a message.
                    mBlocked = false;
                    if(prevMsg ! = null) { prevMsg.next = msg.next; }else {
                        mMessages = msg.next;
                    }
                    msg.next = null;
                    if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                    msg.markInUse();
                    returnmsg; }}else {
                // No more messages.
                nextPollTimeoutMillis = -1;
            }

            // Process the quit message now that all pending messages have been handled.
            if (mQuitting) {
                dispose();
                return null;
            }

            // If first time idle, then get the number of idlers to run.
            // Idle handles only run if the queue is empty or if the first message
            // in the queue (possibly a barrier) is due to be handled in the future.
            if (pendingIdleHandlerCount < 0
                    && (mMessages == null || now < mMessages.when)) {
                pendingIdleHandlerCount = mIdleHandlers.size();
            }
            if (pendingIdleHandlerCount <= 0) {
                // No idle handlers to run.  Loop and wait some more.
                mBlocked = true;
                continue;
            }

            if (mPendingIdleHandlers == null) {
                mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
            }
            mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
        }

        // Run the idle handlers.
        // We only ever reach this code block during the first iteration.
        for (int i = 0; i < pendingIdleHandlerCount; i++) {
            final IdleHandler idler = mPendingIdleHandlers[i];
            mPendingIdleHandlers[i] = null; // release the reference to the handler

            boolean keep = false;
            try {
                keep = idler.queueIdle();
            } catch (Throwable t) {
                Log.wtf(TAG, "IdleHandler threw exception", t);
            }

            if(! keep) { synchronized (this) { mIdleHandlers.remove(idler); }}}... }}Copy the code

The implementation details don’t matter, the next method is an infinite loop unless marked as, or the next Message is found.


1.2 which

Looper is a task handler that creates a Looper on the current thread by calling looper.prepare() and setting a new Looper object on the current thread via ThreadLocal. The looper constructor creates a messageQueue and a Thread.

The current thread has no looper.

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.
    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 block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }

        // This must be in a local variable, in case a 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 traceTag = me.mTraceTag;
        long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
        long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;
        if (thresholdOverride > 0) {
            slowDispatchThresholdMs = thresholdOverride;
            slowDeliveryThresholdMs = thresholdOverride;
        }
        final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0);
        final boolean logSlowDispatch = (slowDispatchThresholdMs > 0);

        final boolean needStartTime = logSlowDelivery || logSlowDispatch;
        final boolean needEndTime = logSlowDispatch;

        if(traceTag ! = 0 && Trace.isTagEnabled(traceTag)) { Trace.traceBegin(traceTag, msg.target.getTraceName(msg)); } final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0; final long dispatchEnd; try { msg.target.dispatchMessage(msg); dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0; } finally {if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
        if (logSlowDelivery) {
            if (slowDeliveryDetected) {
                if ((dispatchStart - msg.when) <= 10) {
                    Slog.w(TAG, "Drained");
                    slowDeliveryDetected = false; }}else {
                if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
                        msg)) {
                    // Once we write a slow delivery log, suppress until the queue drains.
                    slowDeliveryDetected = true; }}}if (logSlowDispatch) {
            showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);
        }

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

        // Make sure that during the course of dispatching the
        // identity of the thread wasn't 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(); }}Copy the code
msg.target.dispatchMessage(msg); This line of code transfers the event to handler's dispatchMessage method to handle the event and is a guarantee that multiple handlers can share a looper. Message carries a reference to its own handler.Copy the code



1.3 Handler

H is just a shell, which does not do much in fact. We know that handler can sendMessage to a message queue. Here’s how it works.


private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}Copy the code

That is, the enqueueMessage method in MessageQueue is called to insert a message into the MessageQueue.


Another point to note is the Handler’s dispatchMessage method

public void dispatchMessage(Message msg) {
    if(msg.callback ! = null) { handleCallback(msg); }else {
        if(mCallback ! = null) {if (mCallback.handleMessage(msg)) {
                return; } } handleMessage(msg); }}Copy the code

This logic checks to see if message has a callback method, and if not, to see if handler has a callback method. HandleMessage is executed if the handler does not have its own callback method.


2.ActivityThread

The ActivityThread is called the “main thread” and also has a Handler that switches the traffic back from the ApplicationThread.

Activitythreads communicate with AMS through applicationThreads for interprocess callback communication.

This main thread Handler defines four components for various starts and stops.

Take a look at the main method source:

public static void main(String[] args) {
    
    Looper.prepareMainLooper();
  
    ActivityThread thread = new ActivityThread();
    thread.attach(false, startSeq);
    if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();
    }
    if (false) {
        Looper.myLooper().setMessageLogging(new
                LogPrinter(Log.DEBUG, "ActivityThread"));
    }
    // End of event ActivityThreadMain.
    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
    Looper.loop();
    throw new RuntimeException("Main thread loop unexpectedly exited");
}Copy the code

Familiar with the stars. The loop ();


3. Summary

When I finally saw the Handler function in ActivityThread, I thought, What a fine layer Android is.

The protocol of the uppermost application layer is determined, and the interfaces are provided by the hardware vendors of the lower layer, and then they are added layer by layer. And ultimately that’s the system that we see. Oh, nonsense, that’s all.