handler

Chiefly used to distribute and process messages

Constructor, initialize handler Looper message queue, etcpublic 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: " +
                    klass.getCanonicalName());
            }
        }

        mLooper = Looper.myLooper();// Initialize looper
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;// Assigning message queue looper initializes the singly linked list structure of message queues
        mCallback = callback;// Whether to implement the interface, a callback interface defined in handler
        mAsynchronous = async;// Whether it is an asynchronous message
    }
Copy the code

Send a message

// Return a runable that finally encapsulates a message as a property of message
   public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }
    // Send a message
     public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }
Copy the code

Finally enter MessageQueue to join the team

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;// Wrap the handler that needs to be processed
        if (mAsynchronous) {
            msg.setAsynchronous(true);// UI draws send asynchronous messages, and sends a message barrier to preferentially process asynchronous messages
        }
        return queue.enqueueMessage(msg, uptimeMillis);/ / team
    }
Copy the code

Process the message

public void dispatchMessage(Message msg) {
        if(msg.callback ! =null) {// MSG Whether to set runable
            handleCallback(msg);
        } else {
            if(mCallback ! =null) {// Whether to implement the interface in handler
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);// We end up with the rewritten handleMessage method}}Copy the code

MessageQueue

Join the team and save the messageboolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {/ / handler is empty
            throw new IllegalArgumentException("Message must have a target.");
        }
        if (msg.isInUse()) {// The message is already in use
            throw new IllegalStateException(msg + " This message is already in use.");
        }

        synchronized (this) {
            if (mQuitting) {// The message queue exits to retrieve the collected message
                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;// Assign message time, wrap message
            Message p = mMessages;// fetch the message head
           // If you need to wake up, Looper loops through next, which automatically assigns mBlocked
            boolean needWake;
			/ / the current queue is empty time 0 | | news message time is less than the head
            if (p == null || when == 0 || when < p.when) {
                // New head, wake up the event queue if blocked.
                msg.next = p;// Insert the queue head next to the old head
                mMessages = msg;//head resets to the current message
                needWake = mBlocked;// Determine whether to wake up based on blocking
            } else {
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
				// The current message time is longer than the head insertion queue
				// Retrieve the previous message that needs to enter and exit the queue. The initial value is head message, i.e. MMessages
                Message prev;
			//例如 初始head 为when 1 next=3; next.next =6;当前msg when = 5
			// prev = 1; p=3; 5 < 3
			// prev=3; p=6; 5<6 break;
                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
					// Indicates that the time is longer than the head time and is already in the middle of the queue
                    if (needWake && p.isAsynchronous()) {
                        needWake = false; }}// Next points to p=6
                msg.next = p; // invariant: p == prev.next 
				// add MSG =3 next to current message 5
                // finally 1,3,5,6
                prev.next = msg;
            }
             // The list has been reordered from smallest to largest by when
            // We can assume mPtr ! = 0 because mQuitting is false.
            if(needWake) { nativeWake(mPtr); }}return true;
    }
Copy the code

Gets the message from the message queue

Message next(a) {
        final long ptr = mPtr;
		// If the message queue is initialized, it will be initialized when it is constructed
        if (ptr == 0) {
            return null;
        }

        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;
        for (;;) {// Infinite loop
            if(nextPollTimeoutMillis ! =0) {
                Binder.flushPendingCommands();
            }
// The blocking method is implemented primarily by native layer epoll listening for write events of file descriptors.
            nativePollOnce(ptr, nextPollTimeoutMillis);

            synchronized (this) {
                // Try to retrieve the next message. Return if found.
                final long now = SystemClock.uptimeMillis();// Get the current time
                Message prevMsg = null;
                Message msg = mMessages;
				// There are three types of messages: synchronous messages, barrier messages and asynchronous messages
                if(msg ! =null && msg.target == null) {// A message barrier has been encountered
                    // Stalled by a barrier. Find the next asynchronous message in the queue.
                    do {
                        prevMsg = msg;
                        msg = msg.next;// Find the first asynchronous message. If there is no message barrier, asynchronous and synchronous messages execute the data in the list in chronological order
                    } while(msg ! =null && !msg.isAsynchronous());
                }
                if(msg ! =null) {
                    if (now < msg.when) {
					// Set the block time to wait for the next round of wake up.
                    // Note that the message is not taken out for processing
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        // Got a message.
						// Less than the current time does not need to block fetch messages to reset the linked list structure
                        mBlocked = false;
						// premsg--> MSG -->msg.next
						// get the message in the middle, the previous MSG and MSG
                        if(prevMsg ! =null) {
                            prevMsg.next = msg.next;
                        } else {
						// return to MSG and reset head to msg.next
                            mMessages = msg.next;
                        }
                        msg.next = null;// return MSG next to null
                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                        msg.markInUse();
                        returnmsg; }}else {
                    // No more messages.
                    nextPollTimeoutMillis = -1;// No message, infinite block, waiting to wake up
                }

                // Process the quit message now that all pending messages have been handled.
                if (mQuitting) {
                    dispose();
                    return null;
                }
// This is a Handler mechanism that allows us to execute tasks when idle during the Looper event loop. The queueIdle() method needs to be implemented when it is defined
                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;// block the queue
                    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);
                    }
                }
            }
            pendingIdleHandlerCount = 0;
            nextPollTimeoutMillis = 0; }}Copy the code

ViewRootImpl uses message barriers for interface drawing, which takes precedence over interface drawing

void scheduleTraversals(a) {
    if(! mTraversalScheduled) { mTraversalScheduled =true;
        // Insert a message barrier to block normal message processing
        mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
        mChoreographer.postCallback(
                Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); }}void doTraversal(a) {
    if (mTraversalScheduled) {
        mTraversalScheduled = false;
        // Remove the message barriermHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier); }}Copy the code

Looper

The main thread parare uses ThreadLocal to store the current looper objectprivate 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));/ / save the stars
    }
Copy the code
The main thread opens loop to read messagespublic static void loop(a) {
        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;

        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();

        final int thresholdOverride =
                SystemProperties.getInt("log.looper."
                        + Process.myUid() + "."
                        + Thread.currentThread().getName()
                        + ".slow".0);

        boolean slowDeliveryDetected = false;

        for (;;) {// Polls for messages
            Message msg = queue.next(); // Get the message from the message queue
            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);// The callback corresponds to the handler method
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } finally {
                if(traceTag ! =0) { Trace.traceEnd(traceTag); }}... Omit some code}Copy the code

Hander callback method

public void dispatchMessage(Message msg) {
        if(msg.callback ! =null) {// MSG Whether to set runable
            handleCallback(msg);
        } else {
            if(mCallback ! =null) {// Whether to implement the interface in handler
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);// We end up with the rewritten handleMessage method}}Copy the code

Message

Message entity classes that retrieve and reuse messages

The main properties/*package*/ int flags;/ / logo

    /*package*/ long when;/ / time

    /*package*/ Bundle data;/ / data

    /*package*/ Handler target;// The handler that processes the message

    /*package*/ Runnable callback;// Executable runable

    // sometimes we store linked lists of these things
    /*package*/ Message next;// The list structure holds a reference to the next MSG
Copy the code

ThreadLocal

The storage uses ThreadLocal as the key and Looper as the valuepublic void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);ThreadLocalMap exists as an attribute of the ThreadLocal static inner class as a whole
        if(map ! =null)
            map.set(this, value);
        else
            createMap(t, value);
    }
    // Get the corresponding thread's looper
     public T get(a) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        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

That’s pretty much the main distribution process