preface

Before we analyze the Android message mechanism in the message Meesage and store the MessageQueue MessageQueue, I believe you have a good understanding of the implementation of these two!! Today, we finally welcome the main character of the entire messaging mechanism, Handler, whose main role is to send messages and process messages.

Normally we inherit the Handler, override the handlerMessage() method to handle the message, call sendMessage() or postMessage() when we need to send the message, and then don’t worry about the internals. But as a responsible and responsible senior technology developer, how can you ignore the internal implementation logic?

Fuck the Source code

  1. When is MSG. Target assigned?
  2. Why is MSG. Target == null an asynchronous message?

Start with the constructor

From the internal method, we can see that there are two cases, parameter with Looper and without Looper, let’s take a look at the construction method with Looper and without Looper respectively

  • Constructor with Looper

    They all end up calling the three-parameter constructor, which you can see is just assigning to the member variable

    public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
    Copy the code
  • Constructor without Looper

    Public Handler(@nullable Callback Callback, Boolean async) {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; }Copy the code

    To create a Handler, we must have a Looper object, otherwise we will throw an exception. This is why we get the Looper object when the child thread creates the Handler object. But why don’t we call looper.prepare () to retrieve the Looper object when we create the Handler for the main thread? This is because the main function of ActivityThread creates Looper objects by default (see the source code for ActivityThread).

    That’s all we have to say about constructors, and that’s really all we have to say about getting Looper objects, which we’ll cover in more detail in the next article.

    Let’s take a look at Handler operations related to getting and sending messages

To get the message

Before sending a Message, we need to have a Message object, and the Handler object gets the Message through the obtainMessage() method, if this is familiar. That’s right, we also have this method in Message, so what does this method in Handler do? Let’s see

@NonNull
public final Message obtainMessage(int what, int arg1, int arg2)
{
    return Message.obtain(this, what, arg1, arg2);
}
Copy the code

This calls message.obtainMessage () directly. Yes, that’s true. See Handler series 1 for details about the mechanism for retrieving Message objects

Now that the message has been retrieved, it’s time to send the message.

Send a message

The most common way to send a message is to send… Or post… The enqueueMessage() method is finally called

private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg, Long uptimeMillis) {// Assign itself to MSG. Target MSG. Target = this; msg.workSourceUid = ThreadLocalWorkSource.getUid(); if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }Copy the code

The source code is fairly clear, first assigning itself to msg.target, and then calling MessageQueue’s enqueueMessage() method to insert the message into the MessageQueue. For information about how message queues process messages, see Handler Series 2

MSG. Target is where the sender assigns its own handler to the target variable of the message

Sending a message is relatively simple, so since the message is sent, is it time to process the message?

Process the message

Handler handles messages with dispatchMessage()

Public void dispatchMessage(@nonnull Message MSG) {// Public void dispatchMessage(@nonnull Message MSG) {// If MSG callback is not null, call handlerCallback() if (msg.callback! = null) { handleCallback(msg); } else {// If (mCallback! = null) { if (mCallback.handleMessage(msg)) { return; } // handleMessage(MSG); }}Copy the code

From the source can be seen in three cases, different ways of processing messages, the next author with you from these three cases respectively to analyze the analysis

MSG callback variable is not null

private static void handleCallback(Message message) {
    message.callback.run();
}
Copy the code
  1. Callbacks to messages are assigned either through the message.obtain () method or directly through setCallback in Message, which is then called for processing.
  2. This way of handling messages is rarely used in normal development

Case 2: The mCallback object is not null

public interface Callback {
    /**
     * @param msg A {@link android.os.Message Message} object
     * @return True if no further handling is desired
     */
    boolean handleMessage(@NonNull Message msg);
}
Copy the code
  1. The mCallback object is also passed in the handler constructor, and if passed in during initialization, the message is handled directly through the interface’s override method handleMessage
  2. Less is used in daily development

Msg. target and mCallback are both null

/**
 * Subclasses must implement this to receive messages.
 */
public void handleMessage(@NonNull Message msg) {
}
Copy the code
  1. Here the handleMessage method is empty
  2. This is the method that we often overwrite when we create Handler objects in our daily development, and we often use it

Remove the message

All removeMessage() and removeCallback() in this Handler are ultimately implemented by calling either mqueue.removemessage or mqueue.removecallBack (), as described in the previous article. See [Handler Series ii] MessageQueue for details

When is MSG. Target null?

//MessageQueue.java 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."); }}Copy the code

When MessageQueue inserts a message, first determine whether MSG. Target is null, if null, directly throw an exception. Msg. target can be null. When is MSG. Target not assigned?

Private int postSyncBarrier(long when) {// Enqueue a new sync barrier token queue because the purpose of a barrier is to stall it. synchronized (this) { final int token = mNextBarrierToken++; // Create a new Message object final Message MSG = message.obtain (); msg.markInUse(); msg.when = when; msg.arg1 = token; Message prev = null; Message p = mMessages; if (when ! = 0) { while (p ! = null && p.when <= when) { prev = p; p = p.next; } } if (prev ! = null) { // invariant: p == prev.next msg.next = p; prev.next = msg; } else { msg.next = p; mMessages = msg; } return token; }}Copy the code

MSG. Target is null. Insert MSG. Target into Message queues

conclusion

  1. Handler Is a process for sending, processing, and removing messages
  2. MSG. Target assigns the current handler value when sendMessage sends the message
  3. The msG. target object in the Message object created when the synchronization barrier is enabled is null
  4. Note that once the synchronization barrier is on, turn it off when appropriate