Understand the complete mechanism of Handler in 10 minutes

Handler principle plain language

4 characters

  • Looper circulator
  • Handler sends and receives messages
  • MessageQueue is right for storing messages
  • Message The message event delivered by Message

Mainly understand the following core important methods from the source code part

1.1Looper.prepare()

  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));
    }

Check whether the looper object exists in the current thread, and create it if it does not exist (ThreadLocal maintains the looper).

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

When the constructor is created, a messageQueue queue will be generated, and the current thread will be bound to the looper at the same time

1.2looper.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;
        
        ........
         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);
            }
            ........

The infinite loop queries whether there is a message in the messageQueue, if not, sleeps and waits for wakeup

try {
                msg.target.dispatchMessage(msg);
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }

1.2 Create handler Handler()

  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: " +
                    klass.getCanonicalName());
            }
        }

        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

Create a Handler in the current thread, get the looper of the current thread at the same time, and get the message queue in the looper of the current thread

1.4 handler sends message Handler.sendMessage

 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);
    }

Bind msg to handler,

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

At the same time, push the message to the messageQueue in the looper, as in 1.5 below.

1.5 Processing message MessageQueue

Mainly used to access the message queue

boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }

2 raises questions

2.1 A looper can be bound to several threads, and a messageQueue can be bound to several loopers

Answer: 1 thread is bound to 1 looper, and there is only one messageQueue in 1 looper.

At the same time, there can be multiple handlers in one thread, which handler will process the message sent by which handler.

2.2 How does the handler delay sending messages? What is the principle?

In looper's loop loop method, the next method in messageQueue will be continuously called [as in the code in 1.2 above]. This method will be inserted according to the actual execution time of the message.

The bottom layer is call sendMessageDelayed, the normal message is delayed by 0 seconds, and the delayed message is delayed by N milliseconds.

2.3 The difference between handlerMessage and dispatchMessage

The bottom layer of dispatchMessage is ultimately called handlerMessage, but in addition it also handles msg and handler with callback separately.

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

Guess you like

Origin blog.csdn.net/admin_jalen/article/details/123157749