This article provides an in-depth understanding of the Android messaging mechanism and source code analysis (Java layer and Native layer). It is recommended to read the article for the sample project, which is linked below:

HandlerDemo

The source code analyzed in this article is based on Android SDK 29 (Android 10.0, i.e. Android Q).

Due to the word limit of the nuggets article, it is divided into two articles:

In-depth understanding of Android messaging and source code analysis (Java layer and Native layer)

In-depth understanding of Android messaging and source code analysis (Java layer and Native layer)

Looper

Looper is used to run a Message loop for the thread, fetching the Message from the MessageQueue and distributing it to the corresponding host Handler for the Message. By default, a thread has no message loop associated with it. To create a loop, call prepare() on the thread running the loop, and then call loop() to let it process the message until the loop stops.

Note that there is only one Looper object per thread.

Java layer

Member variables

Looper class member variables, source code as follows:

// Looper.java
/ / label
private static final String TAG = "Looper";

// Use ThreadLocal to store Looper objects so that each thread can access its own variable copy of the Looper object
@UnsupportedAppUsage
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
// Looper object for the main thread
@UnsupportedAppUsage
private static Looper sMainLooper;
// The observer of the current Looper object
private static Observer sObserver;

// The message queue of the current Looper object
@UnsupportedAppUsage
final MessageQueue mQueue;
// The thread of the current Looper object
final Thread mThread;

// Print text
@UnsupportedAppUsage
private Printer mLogging;
// Trace the tag
private long mTraceTag;

// If the message is sent for longer than that time, a warning log is displayed
private long mSlowDispatchThresholdMs;

// If the message delivery time exceeds this time, a warning log is displayed
private long mSlowDeliveryThresholdMs;
Copy the code

prepare()

The prepare() method initializes the Looper object on the current thread.

// Looper.java
// Initialize the Looper object in a normal thread
public static void prepare(a) {
    // Initialize a Looper object that can exit Looper
    prepare(true);
}

// Private Looper object initialization method. The form parameter quitAllowed is whether to exit Looper
private static void prepare(boolean quitAllowed) {
    if(sThreadLocal.get() ! =null) {
        // Only one Looper object per thread, otherwise RuntimeException is thrown
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    // Store the Looper object into a ThreadLocal object
    sThreadLocal.set(new Looper(quitAllowed));
}

// Initialize the Looper object in the main thread. Note that we do not need to manually call this method to initialize the Looper object in the main thread, since it is already created by the Android system
public static void prepareMainLooper(a) {
    // Initialize a Looper object that cannot exit Looper
    prepare(false);
    // Take Looper's Class object as the lock object
    synchronized (Looper.class) {
        if(sMainLooper ! =null) {
            Throw IllegalStateException if Looper objects have already been created on the main thread
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        // Call myLooper() to get the Looper object associated with the current thread and assign it to the member variable sMainLoopersMainLooper = myLooper(); }}// Returns the Looper object associated with the current thread
public static @Nullable Looper myLooper(a) {
    // Remove the Looper object associated with the current thread from the ThreadLocal object
    return sThreadLocal.get();
}

// Returns the MessageQueue object corresponding to the Looper object associated with the current thread
public static @NonNull MessageQueue myQueue(a) {
    return myLooper().mQueue;
}
Copy the code

loop()

The loop() method runs the message queue in the current thread.

// Looper.java
public static void loop(a) {
    // Looper object associated with the current thread
    final Looper me = myLooper();
    if (me == null) {
        // If the Looper object is not initialized, a RuntimeException is thrown
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    // Message queue corresponding to the Looper object associated with the current thread
    final MessageQueue queue = me.mQueue;

    // Get the unique identifier (uid and PID) of the current thread, which is used to determine whether the thread has been switched for each loop below
    Binder.clearCallingIdentity();
    final long ident = Binder.clearCallingIdentity();

    / / allow the use of system properties to cover threshold, such as: adb shell 'setprop improve log. Stars. 1000. The main. Missile 1 && stop & & start'
    final int thresholdOverride =
            SystemProperties.getInt("log.looper."
                    + Process.myUid() + "."
                    + Thread.currentThread().getName()
                    + ".slow".0);

    boolean slowDeliveryDetected = false;

    // Execute the loop
    for (;;) {
        Fetch the message from the message queue, which may block the thread
        Message msg = queue.next();
        if (msg == null) {
            // If the message is empty, the message queue has exited
            return;
        }

        // Prints logs
        final Printer logging = me.mLogging;
        if(logging ! =null) {
            logging.println(">>>>> Dispatching to " + msg.target + "" +
                    msg.callback + ":" + msg.what);
        }
        // Make sure that the observer does not change while processing the transaction
        final Observer observer = sObserver;

        // Performance analysis related logic
        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;
        Object token = null;
        if(observer ! =null) {
            // If the observer is not empty, call the observer's messageDispatchStarting() method to get the token, which is called before the message is sent
            token = observer.messageDispatchStarting();
        }
        long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
        try {
            // Call the host Handler's dispatchMessage(Message MSG) method to distribute messages to the host Handler
            msg.target.dispatchMessage(msg);
            if(observer ! =null) {
                // If the observer is not empty, the observer's MessageToken (Message MSG) method is called when the Message is processed
                observer.messageDispatched(token, msg);
            }
            dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
        } catch (Exception exception) {
            if(observer ! =null) {
                / / if an Exception occurs, and the observer is not null, call the observer dispatchingThrewException (Object token, Message MSG, Exception Exception) method, it will throw an Exception when processing messages when called
                observer.dispatchingThrewException(token, msg, exception);
            }
            throw exception;
        } finally {
            ThreadLocalWorkSource is used to track who triggered the work currently executed on this thread, call the Store (long token) method, and restore the state with the supplied token
            ThreadLocalWorkSource.restore(origWorkSource);
            if(traceTag ! =0) {
                // Performance analysis related logicTrace.traceEnd(traceTag); }}if (logSlowDelivery) {
            // If the message delivery time exceeds this time, a warning log is displayed
            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) {
            // If the message delivery time exceeds the time limit, a warning log is displayed
            showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);
        }

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

        final long newIdent = Binder.clearCallingIdentity();
        if(ident ! = newIdent) {// If the thread in the loop is different from the one that started the call, the log is printed
            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);
        }

        // After the message is distributed, the message is recycled to the message poolmsg.recycleUnchecked(); }}Copy the code

quit()

Quit () is used to quit Looper.

// Looper.java
// Exit Looper. Note that any attempt to send a Message to the Message queue will fail after Looper is asked to exit, for example, the sendMessage(Message MSG) method returns false
public void quit(a) {
    mQueue.quit(false);
}

// Exit Looper safely. Note that any attempt to send a Message to the Message queue after Looper is asked to exit will fail, for example, the sendMessage(Message MSG) method returns false
public void quitSafely(a) {
    mQueue.quit(true);
}
Copy the code

Call the **quit(Boolean safe)** method of MessageQueue, which was explained earlier and won’t be described here.

Native layer

Looper.h file defines three structures, namely the Request structure, the Response structure and the MessageEnvelope structure, the source code is as follows:

// system/core/libutils/include/utils/Looper.h
// Request structure
struct Request {
    // File descriptor
    int fd;
    POLL_CALLBACK (-2) POLL_CALLBACK (-2) POLL_CALLBACK (-2)
    int ident;
    // The type of file to listen for. The default is EVENT_INPUT
    int events;
    int seq;
    // This callback function is called when an event occurs
    sp<LooperCallback> callback;
    // Data in the file descriptor
    void* data;

    void initEventItem(struct epoll_event* eventItem) const;
};

// Response structure
struct Response {
    // The file type. The default is EVENT_INPUT
    int events;
    / / Request object
    Request request;
};

// The envelope structure
struct MessageEnvelope {
    MessageEnvelope() : uptime(0) {}MessageEnvelope(nsecs_t u, sp<MessageHandler> h, const Message& m)
        : uptime(u), handler(std::move(h)), message(m) {}

    // Send time
    nsecs_t uptime;
    // The envelope receiver
    sp<MessageHandler> handler;
    // The envelope contents
    Message message;
};
Copy the code

The constructor

The source code for the looper.cpp file section is as follows:

// system/core/libutils/Looper.cpp
// The maximum number of file descriptors required to retrieve polling events per iteration is 16
static const int EPOLL_MAX_EVENTS = 16;

// The Looper constructor
Looper::Looper(bool allowNonCallbacks)
    : mAllowNonCallbacks(allowNonCallbacks),
      mSendingMessage(false),
      mPolling(false),
      mEpollRebuildRequired(false),
      mNextRequestSeq(0),
      mResponseIndex(0),
      mNextMessageUptime(LLONG_MAX) {
    The eventfd(unsigned int __initial_value, int __flags) function is used to create an event object. It returns a file descriptor to represent the event object, which we can use to call the object
    mWakeEventFd.reset(eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC));
    LOG_ALWAYS_FATAL_IF(mWakeEventFd.get()"0."Could not make wake event fd: %s".strerror(errno));

    // the function AutoMutex _l uses the C++ constructor and destructor to automatically lock and release the lock
    AutoMutex _l(mLock);
    // Rebuild the mPoll event
    rebuildEpollLocked(a); }Copy the code

The logic executed in Looper’s constructor was explained previously and won’t be repeated here.

SendMessage starts a series of functions

The source code is as follows:

// system/core/libutils/Looper.cpp
void Looper::sendMessage(const sp<MessageHandler>& handler, const Message& message) {
    // Get the system time
    nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
    / / call stars: : sendMessageAtTime (nsecs_t uptime, const sp < MessageHandler > & handler,
        constThe Message & Message) functionsendMessageAtTime(now, handler, message);
}

void Looper::sendMessageDelayed(nsecs_t uptimeDelay, const sp<MessageHandler>& handler,
        const Message& message) {
    // Get the system time
    nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
    / / call stars: : sendMessageAtTime (nsecs_t uptime, const sp < MessageHandler > & handler,
        constThe Message & Message) functionsendMessageAtTime(now + uptimeDelay, handler, message);
}

void Looper::sendMessageAtTime(nsecs_t uptime, const sp<MessageHandler>& handler,
        const Message& message) {
#if DEBUG_CALLBACKS
    ALOGD("%p ~ sendMessageAtTime - uptime=%" PRId64 ", handler=%p, what=%d".this, uptime, handler.get(), message.what);
#endif

    size_t i = 0;
    {
        / / lock
        AutoMutex _l(mLock);

        size_t messageCount = mMessageEnvelopes.size(a);while (i < messageCount && uptime >= mMessageEnvelopes.itemAt(i).uptime) {
            // Count index I chronologically
            i += 1;
        }

        // Create the envelope and pass in the corresponding parameters
        MessageEnvelope messageEnvelope(uptime, handler, message);
        // Insert the envelope into the envelope Vector according to index I
        mMessageEnvelopes.insertAt(messageEnvelope, i, 1);

        if (mSendingMessage) {
            If a message is being sent, the wake() function is not called to wake up the poll loop and terminate the function
            return; }}/ / releases the lock

    if (i == 0) {
        // If I equals 0, the message is inserted into the head of the envelope vector, and the poll loop is awakened by calling wake()
        wake();
    }
}
Copy the code

The callback class

The source code for the LooperCallback class is as follows:

// system/core/libutils/include/utils/Looper.h
class LooperCallback : public virtual RefBase {
protected:
    // LooperCallback() is a virtual destructor
    virtual ~LooperCallback(a);public:
    // Handles the poll event for a given file descriptor, which provides the file descriptor associated with it, the bitmask (usually EVENT_INPUT) of the poll event that was triggered, and the data pointer originally provided
    virtual int handleEvent(int fd, int events, void* data) = 0;
};
Copy the code

The SimpleLooperCallback class extends from the LooperCallback class. The source code for the SimpleLooperCallback class is as follows:

// system/core/libutils/include/utils/Looper.h
class SimpleLooperCallback : public LooperCallback {
protected:
    // SimpleLooperCallback() is the virtual destructor
    virtual ~SimpleLooperCallback(a);public:
    SimpleLooperCallback(Looper_callbackFunc callback);
    // handleEvent(int fd, int events, void* data) is a virtual function
    virtual int handleEvent(int fd, int events, void* data);

private:
    // The callback function
    Looper_callbackFunc mCallback;
};
Copy the code

**handleEvent(int fd, int events, void* data)**

// system/core/libutils/Looper.cpp
int SimpleLooperCallback::handleEvent(int fd, int events, void* data) {
    // Call the callback function
    return mCallback(fd, events, data);
}
Copy the code

Handler

Handler is used to process messages, send message events to the message pool, and process message events.

Java layer

A constructor

The Handler constructor looks like this:

// Handler.java
// The default constructor used to associate this Handler with the current thread's Looper object
public Handler(a) {
    this(null.false);
}

// Constructor that associates this Handler with the current thread's Looper object and accepts a callback interface that can handle messages. The callback argument can be null
public Handler(@Nullable Callback callback) {
    this(callback, false);
}

// Constructor that associates this Handler with the Looper object passed in. The Looper argument cannot be null
public Handler(@NonNull Looper looper) {
    this(looper, null.false);
}

// Constructor that associates this Handler with the passed Looper object and accepts a callback interface that can handle the message. The Looper argument cannot be null, and the callback argument can be null
public Handler(@NonNull Looper looper, @Nullable Callback callback) {
    this(looper, callback, false);
}

Handlers are synchronous by default. Call this method to construct an asynchronous Handler. Note that this method is not supported for use by external applications
public Handler(boolean async) {
    this(null, async);
}
Copy the code

The constructor above will eventually call the following two methods, with the source code shown below:

// Handler.java
public Handler(@Nullable Callback callback, boolean async) {
    if (FIND_POTENTIAL_LEAKS) {
        // If there is a risk of a memory leak, execute the following logic
        final Class<? extends Handler> klass = getClass();
        if((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) && (klass.getModifiers() & Modifier.STATIC) = =0) {
            // If it is an anonymous class, an inner class, or a local class, it must be declared static, otherwise a warning will be printed about a possible memory leak
            Log.w(TAG, "The following Handler class should be static or leaks might occur: "+ klass.getCanonicalName()); }}// Get the Looper object of the current thread
    mLooper = Looper.myLooper();
    if (mLooper == null) {
        // Throw a RuntimeException if the current thread's Looper object is empty, that is, Looper has not been initialized
        throw new RuntimeException(
            "Can't create handler inside thread " + Thread.currentThread()
                    + " that has not called Looper.prepare()");
    }
    // Get the MessageQueue corresponding to the current thread's Looper object
    mQueue = mLooper.mQueue;
    // Assign the formal argument callback to the member variable mCallback
    mCallback = callback;
    // Assign the formal parameter asyn to the member variable mAsynchronous
    mAsynchronous = async;
}

// Use the Looper object passed in instead of the default, accept a callback interface to handle messages, and set whether the Handler should be asynchronous. Note that this method is not supported by external applications
@UnsupportedAppUsage
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
    mLooper = looper;
    mQueue = looper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}
Copy the code

Send a message

There are two main types of sending messages:

  • The method that starts with POST
  • Method starting with sendMessage

Take a look at the relevant source code, the source code is as follows:

// Handler.java
// Add the Runnable passed in to the end of the message queue. The Runnable will run on the thread attached to the Handler
public final boolean post(@NonNull Runnable r) {
   return  sendMessageDelayed(getPostMessage(r), 0);
}

// Add the Runnable passed in to the end of the message queue and run it in uptimeMillis time on the thread attached to the Handler
public final boolean postAtTime(@NonNull Runnable r, long uptimeMillis) {
    return sendMessageAtTime(getPostMessage(r), uptimeMillis);
}

// Add the Runnable passed in to the end of the message queue and run it in uptimeMillis time. Token is a token, It in order to call removeCallbacksAndMessages (Object token) method can delete message queue corresponding message, the Runnable will be run on additional threads in the Handler
public final boolean postAtTime(
        @NonNull Runnable r, @Nullable Object token, long uptimeMillis) {
    return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
}

// Add the Runnable passed in to the end of the message queue and run after delayMillis time. The Runnable will run on the thread attached to the Handler
public final boolean postDelayed(@NonNull Runnable r, long delayMillis) {
    return sendMessageDelayed(getPostMessage(r), delayMillis);
}

// Add the Runnable passed in to the end of the message queue and run it after delayMillis time with the message category what set. The Runnable will run on the thread attached to the Handler. Note that this method does not support external application calls
public final boolean postDelayed(Runnable r, int what, long delayMillis) {
    return sendMessageDelayed(getPostMessage(r).setWhat(what), delayMillis);
}

// Add the Runnable passed in to the end of the message queue and run after delayMillis. Token is a token, It in order to call removeCallbacksAndMessages (Object token) method can delete message queue corresponding message, the Runnable will be run on additional threads in the Handler
public final boolean postDelayed(
        @NonNull Runnable r, @Nullable Object token, long delayMillis) {
    return sendMessageDelayed(getPostMessage(r, token), delayMillis);
}

// Add the Runnable passed in to the header of the message queue. The Runnable will run on the thread attached to the Handler
public final boolean postAtFrontOfQueue(@NonNull Runnable r) {
    return sendMessageAtFrontOfQueue(getPostMessage(r));
}

// Add incoming Message to the end of the Message queue
public final boolean sendMessage(@NonNull Message msg) {
    return sendMessageDelayed(msg, 0);
}

// Add an empty normal message to the end of the message queue and set the message category for the message what
public final boolean sendEmptyMessage(int what)
{
    return sendEmptyMessageDelayed(what, 0);
}

// Add an empty delayMillis normal message to the end of the message queue and set the message category for this message what
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
    Message msg = Message.obtain();
    msg.what = what;
    return sendMessageDelayed(msg, delayMillis);
}

// Add an empty normal message running in uptimeMillis time to the end of the message queue and set the message category for this message what
public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
    Message msg = Message.obtain();
    msg.what = what;
    return sendMessageAtTime(msg, uptimeMillis);
}

// Add a normal message delayed delayMillis to the end of the message queue
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

// Add a normal message running in uptimeMillis time to the end of the message queue
public boolean sendMessageAtTime(@NonNull 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);
}

// Add a normal message to the header of the message queue
public final boolean sendMessageAtFrontOfQueue(@NonNull 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);
}

// If the Message is called on the same thread as the Handler, the Message is executed synchronously, otherwise, the sendMessage(Message MSG) method is called to add it to the Message queue, and returns true if the Message was successfully executed or added to the Message queue, otherwise, Returns false, usually because the Looper handling the message exits
public final boolean executeOrSendMessage(@NonNull Message msg) {
    if (mLooper == Looper.myLooper()) {
        dispatchMessage(msg);
        return true;
    }
    return sendMessage(msg);
}
Copy the code

GetPostMessage (Runnable R) or getPostMessage(Runnable R, Object token). In fact, the method starting with POST is essentially the same as the method starting with sendMessage, which is to add a normal message to the message queue, the source code is as follows:

// Handler.java
// Retrieves the message from the message pool, sets the message callback to the Runnable passed in, and returns the message
private static Message getPostMessage(Runnable r) {
    Message m = Message.obtain();
    m.callback = r;
    return m;
}

// Retrieves the message from the message pool, and sets the message's obj to the token passed in, callback to the Runnable passed in, and returns the message
@UnsupportedAppUsage
private static Message getPostMessage(Runnable r, Object token) {
    Message m = Message.obtain();
    m.obj = token;
    m.callback = r;
    return m;
}
Copy the code

Method of sending messages over the last invoked sendMessageAtTime (Message MSG, long uptimeMillis) methods or sendMessageAtFrontOfQueue (Message MSG) method, The two methods then call enqueueMessage(MessageQueue Queue, Message MSG, Long uptimeMillis), The last method calls the **enqueueMessage(Message MSG, long WHEN)** method of MessageQueue.

// Handler.java
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
        long uptimeMillis) {
    // Set the receiver of the message to itself
    msg.target = this;
    // Set the uid for the message to enter the queue
    msg.workSourceUid = ThreadLocalWorkSource.getUid();

    if (mAsynchronous) {
        // If asynchronous, set the message to asynchronous
        msg.setAsynchronous(true);
    }
    // Call the enqueueMessage(Message MSG, long When) method of MessageQueue
    return queue.enqueueMessage(msg, uptimeMillis);
}
Copy the code

dispatchMessage(Message msg)

The dispatchMessage(Message MSG) method is used to send messages.

// Handler.java
public void dispatchMessage(@NonNull Message msg) {
    if(msg.callback ! =null) {
        // If msg.callback is not empty, i.e. a Message is added to the Message queue via the method starting with POST, the handleCallback(Message Message) method is called
        handleCallback(msg);
    } else {
        // If msg.callback is empty, that is, the message is added to the message queue using the method beginning with sendMessage, the following logic is performed
        if(mCallback ! =null) {
            // If the member variable mCallback is not empty, the Handler sets the Callback and calls the handleMessage(Message MSG) method of mCallback
            if (mCallback.handleMessage(msg)) {
                If the handleMessage(Message MSG) method returns true, the Message has been intercepted and no further processing is required, terminating the method
                return; }}// If the handleMessage(Message MSG) method returns false, the Message was not intercepted and needs further processing, call the handleMessage(Message MSG) methodhandleMessage(msg); }}Copy the code

**handleCallback(Message Message)** if a Message is added to a Message queue using a method that starts with POST, it is called:

// Handler.java
private static void handleCallback(Message message) {
    // Call the run() method of the message's Runnable
    message.callback.run();
}
Copy the code

If a Message is added to a Message queue using the method starting with sendMessage, either the handleMessage(Message MSG) or handleMessage(Message MSG) method of the Handler’s internal interface Callback is called. These two methods need to be implemented by the developers themselves, the source code is as follows:

// Handler.java
public interface Callback {
    // This method needs to be implemented by the developer. If this method returns true, the message has been intercepted; If this method returns false, the message was not intercepted and requires further processing
    boolean handleMessage(@NonNull Message msg);
}

// This method needs to be implemented by the developers themselves
public void handleMessage(@NonNull Message msg) {}Copy the code

Handle Message priorities

The priority of processing messages, from top to bottom and from low to high, is as follows:

  • Handler’s handleMessage(Message MSG) method: Adds **Message to MessageQueue ** using the method beginning with sendMessage.
  • The handleMessage(Message MSG) method is used to set the Handler’s internal interface, Callback.
  • The run() method of the Message member variable callback (Runnable) : Adds Message to the **MessageQueue (Message queue) ** via the method beginning with POST.

ObtainMessage series of methods

The obtainMessage family of methods is used to retrieve messages from the message pool. The source code is as follows:

// Handler.java
// Retrieve the message from the message pool
@NonNull
public final Message obtainMessage(a)
{
    return Message.obtain(this);
}

// Retrieves the message of type WHAT from the message pool
@NonNull
public final Message obtainMessage(int what)
{
    return Message.obtain(this, what);
}

// Fetch the message category from the message pool is what, and the message content is obJ's message
@NonNull
public final Message obtainMessage(int what, @Nullable Object obj) {
    return Message.obtain(this, what, obj);
}

// Retrieve the message from the message pool with the message category of what and the arguments arg1 and arg2
@NonNull
public final Message obtainMessage(int what, int arg1, int arg2)
{
    return Message.obtain(this, what, arg1, arg2);
}

// Retrieve the message type from the message pool is what, with arguments arg1 and arg2, and the message content is obj
@NonNull
public final Message obtainMessage(int what, int arg1, int arg2, @Nullable Object obj) {
    return Message.obtain(this, what, arg1, arg2, obj);
}
Copy the code

All of these methods end up calling the Message class’s Obtain methods, which return a new Message object from the Message pool and avoid reassigning new Message objects, as explained earlier and not described here.

Delete the message

Deleting messages falls into two classes of methods:

  • RemoveCallbacks family of methods
  • RemoveMessages series methods

The source code is as follows:

// Handler.java
// Delete the Runnable ordinary message from the message queue
public final void removeCallbacks(@NonNull Runnable r) {
    mQueue.removeMessages(this, r, null);
}

// Remove Runnable normal messages with token tokens from message queues
public final void removeCallbacks(@NonNull Runnable r, @Nullable Object token) {
    mQueue.removeMessages(this, r, token);
}

// Delete the normal message in the message queue whose message category is WHAT
public final void removeMessages(int what) {
    mQueue.removeMessages(this, what, null);
}

// Delete a normal message in the message queue whose category is WHAT and whose content is Object
public final void removeMessages(int what, @Nullable Object object) {
    mQueue.removeMessages(this, what, object);
}

// Remove all ordinary messages with token tokens from the message queue. Note that all ordinary messages in the message queue are deleted if the token token is passed empty
public final void removeCallbacksAndMessages(@Nullable Object token) {
    mQueue.removeCallbacksAndMessages(this, token);
}
Copy the code

MessageQueue removeMessages(Handler H, int What, Object Object) It removes the normal messages in the message queue that meet the criteria. This method has been explained previously and will not be repeated here.

Native layer

The source code for the MessageHandler class is as follows:

// external/webrtc_legacy/webrtc/base/messagehandler.h
class MessageHandler {
 public:
  // MessageHandler() is a virtual destructor
  virtual ~MessageHandler(a);// onMessage(Message* MSG) is a virtual function
  virtual void OnMessage(Message* msg) = 0;

 protected:
  MessageHandler() {}

 private:
  RTC_DISALLOW_COPY_AND_ASSIGN(MessageHandler);
};
Copy the code

WeakMessageHandler class inherits The MessageHandler class, the role of WeakMessageHandler class is to hold the simple proxy of weak reference to MessageHandler, WeakMessageHandler class source code is shown as follows:

// system/core/libutils/include/utils/Looper.h
class WeakMessageHandler : public MessageHandler {
protected:
    // WeakMessageHandler() is a virtual destructor
    virtual ~WeakMessageHandler(a);public:
    WeakMessageHandler(const wp<MessageHandler>& handler);
    // The handleMessage(const Message& Message) function is virtual
    virtual void handleMessage(const Message& message);

private:
    wp<MessageHandler> mHandler;
};
Copy the code

**handleMessage(const Message& Message)**

// system/core/libutils/Looper.cpp
void WeakMessageHandler::handleMessage(const Message& message) {
    sp<MessageHandler> handler = mHandler.promote(a);if(handler ! =nullptr) {
        // If the strong pointer to MessageHandler is not null, the handleMessage(const Message& Message) function of MessageHandler is called
        handler->handleMessage(message); }}Copy the code

Why doesn’t the main thread get stuck because of the dead loop in looper.loop ()?

Before answering this question, let’s explain the concept of processes and threads:

Process is a running activity of code on the data set. It is the basic unit of system resource allocation and scheduling. A process has at least one thread. A thread is an entity in the process. The thread itself does not exist independently.

Each application process forks from an existing process called Zygote. The system starts and loads common Framework code and resources (for example: In order to start a new application process, the system forks the Zygote process and loads and runs the reference code in the new process. This approach allows most RAM pages for Framework code and resource allocation to be shared between all application processes, most of which run in one process, You can also set the Android: Process property in anroidmanifest.xml or create multiple processes by forking them in native code. For more information, check out the Android Memory management article.

A Thread is an instance of the java.lang.Thread class that has executed the start() Method and has not yet finished. All key methods of Thread are Native methods. This means that these methods are not implemented or cannot be implemented using platform-specific means.

In Linux, there is no essential difference between a process and a thread, except whether or not they share resources. To the CPU, they are a piece of executable code, which is a task_struct structure. The CPU uses Completely Fair Scheduler (CFS) algorithm. This algorithm ensures that each task has as fair a slice of CPU time as possible.

Because the thread is a executable code, when a thread after completion of execution, which is an executable code execution is completed, the thread running state came into the end state (Terminated), then a thread will be Terminated, for the main thread, the system does not want the main thread running after a period of time, the main thread will be Terminated, that how to make the main thread is not Terminated? Binder threads also use an infinite loop to read and write to their drivers, not just in an infinite loop. This involves the Pipe /epoll mechanism of Linux. In the main thread, when MessageQueue has no Message, it blocks in the nativePollOnce() method of the next() method of the MessageQueue class, at which point the main thread frees CPU resources and goes to sleep. Wake up the main thread by writing data to the pipe writer until the next message arrives or a transaction occurs. Epoll is a readable signal that notifies the kernel buffer of a file descriptor when it is not empty. An I/O multiplexing mechanism that monitors multiple file descriptors at the same time. The loop() method of the Looper class also does not cause the application to freeze because the Activity’s lifecycle method takes too long to operate, causing frames to drop, or even an application to become unresponsive (ANR). Therefore, the main thread is idle most of the time, and does not consume a lot of CPU resources, nor does it cause the application to freeze.

conclusion

The developer adds a Message to the MessageQueue by calling either the Handler’s POST or sendMessage methods. The loop() method of Looper is used to fetch messages that meet the trigger conditions from MessageQueue continuously, and then the dispatchMessage(Message MSG) method of host Handler is called. According to the conditions, If a Message is added to a MessageQueue using a method starting with POST, the run() method of Runnable is called; If MessageQueue is added to MessageQueue via sendMessage, check whether the constructor is used to set the Handler’s internal interface Callback. Call the handleMessage(Message MSG) method of the Handler’s internal interface, Callback, and let the developer handle the Message. If not, the handleMessage(Message MSG) method is called to let the developer handle the Message. Note that MessageQueue is idle when there is no Message in it. Call the **queueIdle()** method of MessageQueue’s internal interface IdleHandler.

MessageQueue is the link between Java layer and Native layer. The MessageQueue of Java layer and The MessageQueue of Native layer are established through JNI (Java Native Interface). Java layer can add Message to MessageQueue, Native layer can also add Message to MessageQueue. The Message, Looper, and Handler classes have no connection between the Java layer and the Native layer. They all implement similar logic in their own layer and are independent of each other. The NativeMessageQueue class of the Native layer inherits the MessageQueue class of the Native layer, and the WeakMessageHandler class of the Native layer inherits the MessageHandler class.

Note that the processing steps for the message are as follows:

  1. Processing Native layer messages
  2. Processing Native layer Request
  3. Process messages for the Java layer

digression

In Android messaging, you can see the use of ThreadLocal to store Looper objects, and HERE I’d like to cover thread-local storage (TLS).

Thread-local Storage (TLS)

Thread-local Storage (TLS) is a Storage Duration in which Storage of an object is allocated at the start of a thread and reclaimed at the end, and each thread has its own instance of the object.

In C, use the keyword **_Thread_local to define the thread-local variable **. In the header file define thread_local as the synonym of this keyword, as shown in the following code:

#include <threads.h>
thread_local int foo = 0;
Copy the code

In C++, the keyword thread_local is used to define thread-local variables.

In Java, the ThreadLocal class is used to provide thread-local variables. Ordinary variables can be read or written by any thread. Variables created by ThreadLocal can only be read or written by the current thread. The inner class of ThreadLocal, ThreadLocalMap, uses the open address method to resolve hash conflicts. If you want to learn more, you can take a look at the Java collection framework – Android HashMap source code analysis article as an off-topic.

My GitHub: TanJiaJunBeyond

Common Android Framework: Common Android framework

My nuggets: Tan Jiajun

My simple book: Tan Jiajun

My CSDN: Tan Jiajun