Android-handler Source code parsing -handler

Member variables

// Check for potential leaks
private static final boolean FIND_POTENTIAL_LEAKS = false;
/ / Log Tag
private static final String TAG = "Handler";
// Main thread Handler
private static Handler MAIN_THREAD_HANDLER = null;

// Looper of this Handler
@UnsupportedAppUsage 
final Looper mLooper;

// This Handler's message queue comes from the Looper object.
final MessageQueue mQueue;

// When this Handler distributes the message, the Callback of the message takes precedence.
@UnsupportedAppUsage
final Callback mCallback;

// Whether to send or POST messages are all asynchronous messages. The default value is false.
final boolean mAsynchronous;

// Cross-process communication, message sender.
@UnsupportedAppUsage
IMessenger mMessenger;
Copy the code

Description:

  1. HandlerWhy you need to holdLooper,MessageQueueBecause theHandlerSend a message, etcOperation needs to knowWhich to sendMessageQueueAnd theMessageQueueNeed fromLooperIn order to obtainSent messageAble toPolling distribution
  2. LooperFor more information, please seeTripartite library -Handler source code parsing -Looper.
  3. MessageQueueFor more information, please seeTripartite library -Handler source code parsing -MessageQueue.

Create a Handler

To use a Handler, you first need to create a Handler, so let’s take a look at how it is created.

new Handler()

The default stars

Handler()

@Deprecated
public Handler(a) {
    this(null.false);
}
Copy the code

Handler(Callback)

@Deprecated
public Handler(@Nullable Callback callback) {
    this(callback, false);
}
Copy the code

Handler(Callback, boolean)

/ * *@hide* /
public Handler(@Nullable Callback callback, boolean async) {
    if (FIND_POTENTIAL_LEAKS) {
		// Check for potential leaks
        final Class<? extends Handler> klass = getClass();
        if((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) && (klass.getModifiers() & Modifier.STATIC) = =0) {
            // Anonymous class, member class, local class, and not static, warning (the following Handler class should be static, otherwise it might leak).
            Log.w(TAG, "The following Handler class should be static or leaks might occur: "+ klass.getCanonicalName()); }}// Get the current thread's Looper
    mLooper = Looper.myLooper();
    if (mLooper == null) {
	    Handler cannot be created in a thread that did not call looper.prepare ().
        throw new RuntimeException(
            "Can't create handler inside thread " + Thread.currentThread()
                    + " that has not called Looper.prepare()");
    }
    // Get the message queue of Looper
    mQueue = mLooper.mQueue;
    / / record the CallBack
    mCallback = callback;
    // Whether the record is asynchronous
    mAsynchronous = async;
}
Copy the code

For the above constructor, use the default Looper (Looper for the current thread). If the thread has no looper, an exception is thrown.

Description:

  1. Handler(),Handler(Callback)Two constructorsHas been markedfor@Deprecated(out of date), because inHandlerTectonic processimplicitSelect aLooperCould lead toerror, where the operation willAutomatic loss(ifHandlerNo need for a new mission andexit),collapse(ifhandlerSometimes in aNo activationLooperthreadoncreate), orA race condition, includinghandlerThe associatedthreadNot what the author expected. Instead, it can be usedjava.util.concurrent.Executor, or usingLooper.getMainLooper, {link android.view.View#getHandler} orSimilar tools are explicitly specifiedLooper. If for the sake ofcompatibilityNeed to hidethread localBehavior, then usenew Handler(Looper.myLooper())Make it clear to the reader.
  2. Handler(Callback, boolean)Two constructors have been marked as@hide(Hidden), onlyWithin the systemUse.
  3. useThe defaulttheLooper(The current threadtheLooper), if thisthreadThere is nolooper,An exception is thrown.
  4. parameterCallbackAnd for this reasonHandlerDistribution of the messageWhen,Give priority tomessage-processingCallback, see below for detailsDistribution of the Message.
  5. parameterasyncAnd for this reasonHandlerSend a messageWhether,allsendAsynchronous/synchronous(the defaultsynchronous) message, see below for detailsSend the Message.
  6. Asynchronous messagingsaidDon’t needrightA synchronous messageforGlobal orderingtheinterruptorThe event.Asynchronous messagingIs not affected byMessageQueue.postSyncBarrier(long)The introduction ofSynchronization barrierThe influence of.
  7. MessageQueueFor an introduction to synchronization barriers, seeTripartite library -Handler source code parsing -MessageQueue- Synchronization barrier.

To specify which

Handler(Looper)

public Handler(@NonNull Looper looper) {
    this(looper, null.false);
}
Copy the code

Handler(Looper, Callback)

public Handler(@NonNull Looper looper, @Nullable Callback callback) {
    this(looper, callback, false);
}
Copy the code

Handler(Looper, Callback, boolean)

/ * *@hide* /
@UnsupportedAppUsage
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
    mLooper = looper;
    mQueue = looper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}
Copy the code

The above constructor uses the specified Looper instead of the default Looper.

Description:

  1. Handler(Looper),Handler(Looper, Callback)Two constructorsNot markedfor@Deprecated(obsolete), and useThe specifiedtheLooper.recommendedUse.
  2. Handler(Looper, Callback, boolean)The constructor has been marked as@hide(Hidden), onlyWithin the systemUse.

Handler.createAsync()

Handler.createAsync(Looper)

@NonNull
public static Handler createAsync(@NonNull Looper looper) {
    if (looper == null) throw new NullPointerException("looper must not be null");
    return new Handler(looper, null.true);
}
Copy the code

Handler.createAsync(Looper, Callback)

@NonNull
public static Handler createAsync(@NonNull Looper looper, @NonNull Callback callback) {
    if (looper == null) throw new NullPointerException("looper must not be null");
    if (callback == null) throw new NullPointerException("callback must not be null");
    return new Handler(looper, callback, true);
}
Copy the code

The handler.createAsync () method, for static methods, creates an asynchronous Handler whose published Message is not affected by synchronization barriers such as displaying vsync.

Description:

  1. Sent to theasynchronoushandlerMessages can be guaranteed to be in order from one another, but not necessarily from anotherHandlersTo sort the messages.

summary

  1. createsynchronousHandler, divided intoIs not specifiedLooper,The specifiedLooperOne of two wayscreate.
  • Is not specifiedLooper, use theThe current threadtheLooper.Is not recommendedUse.
  • The specifiedLooper.recommendedWith, the constructor isHandler(Looper),Handler(Looper, Callback).
  1. createasynchronousHandler, the use ofHandler.createAsync(Looper),Handler.createAsync(Looper, Callback)methodscreate.

Create a Message

To use a Message, you can create a Message with a Handler, so let’s take a look at how it is created.

obtainMessage()

@NonNull
public final Message obtainMessage(a) {
    return Message.obtain(this);
}

@NonNull
public final Message obtainMessage(int what) {
    return Message.obtain(this, what);
}

@NonNull
public final Message obtainMessage(int what, @Nullable Object obj) {
    return Message.obtain(this, what, obj);
}

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

@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

The obtainMessage() method above returns a new message from the global message pool. Internally, the Message is created using the message.obtain () method and the target of its Message is the current Handler.

Description:

  1. Message.obtain()For more information, please seeTripartite library -Handler source code parsing -Message- create Message.

summary

  1. handler.obtainMessage()Method, usingMessage.obtain()methodsCreate a messageAnd should beMessagethetargetFor the currentHandler.

Send the Message

After Message is created, the Message can be sent. In addition to sending the Message via sendMessage(), the Handler can also perform the specified Callback task via POST (), so let’s take a look at how they are sent.

send-Message

sendMessage()

public final boolean sendMessage(@NonNull Message msg) {
    return sendMessageDelayed(msg, 0);
}
Copy the code

sendEmptyMessage()

public final boolean sendEmptyMessage(int what) {
    return sendEmptyMessageDelayed(what, 0);
}
Copy the code

sendEmptyMessageDelayed()

public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
    Message msg = Message.obtain();
    msg.what = what;
    return sendMessageDelayed(msg, delayMillis);
}
Copy the code

sendEmptyMessageAtTime()

public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
    Message msg = Message.obtain();
    msg.what = what;
    return sendMessageAtTime(msg, uptimeMillis);
}
Copy the code

sendMessageDelayed()

public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
Copy the code

sendMessageAtTime()

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);
}
Copy the code

sendMessageAtFrontOfQueue()

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);
}
Copy the code

The above methods include sending immediate messages, sending delayed messages, sending time-specific messages, and placing messages at the front of the message queue.

Description:

  1. sendMessage(),sendEmptyMessage()forSend instant messageThe message,sendMessageDelayed(),sendEmptyMessageDelayed()forSending delayed messagesThe message,sendMessageAtTime()forSends a specified time message.sendMessageAtFrontOfQueue()forQueue messages to the front of the message queue.
  2. sendMessageAtFrontOfQueue()forQueue messages to the front of the message queueSo that in the messagelooptheThe next timeIterative processing. This methodUsed only for very special purposesIt’s easy to makeMessage queue starvation,Cause sorting problemsorTo produce otherunexpectedSide effects.
  3. Send instant message,Sending delayed messages,Send at specified time,Queue messages to the front of the message queueAll they end up calling isenqueueMessage()Methods.

Let’s look at the enqueueMessage() method next.

enqueueMessage()

private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
        long uptimeMillis) {
    // Specify a Message Handler for this Handler
    msg.target = this;
    msg.workSourceUid = ThreadLocalWorkSource.getUid();

	// If this Handler is asynchronous, all messages sent are asynchronous.
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    // use MessageQueue to add messages to the MessageQueue
    return queue.enqueueMessage(msg, uptimeMillis); 
}
Copy the code

Method enqueueMessage() to add a Message to a MessageQueue.

Description:

  1. The specifiedthisMessagethetargetforthisHandlerTo makethisMessageandthisHandlerAssociation for usethisHandlerTo deal withthisMessage.
  2. If thisHandlerisasynchronousIs sentAll of the messagesAre allasynchronous.
  3. uptimeMillisParameters forMessage execution time.Executed immediatelyFor theSystemClock.uptimeMillis().Delay to performFor theSystemClock.uptimeMillis() + delayMillisSend at specified timeFor theThe specified.Queue messages to the front of the message queueFor the0.
  4. MessageQueueAdd message related introduction, seeTripartite library -Handler source parsing -MessageQueue- Add Message.

post-Callback

post()

public final boolean post(@NonNull Runnable r) {
   return  sendMessageDelayed(getPostMessage(r), 0);
}
Copy the code

postAtTime()

public final boolean postAtTime(@NonNull Runnable r, long uptimeMillis) {
    return sendMessageAtTime(getPostMessage(r), uptimeMillis);
}

public final boolean postAtTime(@NonNull Runnable r, @Nullable Object token, long uptimeMillis) {
    return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
}

Copy the code

postDelayed()

public final boolean postDelayed(@NonNull Runnable r, long delayMillis) {
    return sendMessageDelayed(getPostMessage(r), delayMillis);
}

public final boolean postDelayed(@NonNull Runnable r, @Nullable Object token, long delayMillis) {
    return sendMessageDelayed(getPostMessage(r, token), delayMillis);
}
Copy the code

postAtFrontOfQueue()

public final boolean postAtFrontOfQueue(@NonNull Runnable r) {
    return sendMessageAtFrontOfQueue(getPostMessage(r));
}
Copy the code

The above methods, which perform the specified Callback task, are implemented by Runnable. The Runnable is wrapped as a Message by getPostMessage() and then sent by calling the corresponding Message sending method.

Let’s look at the implementation of the getPostMessage() method.

getPostMessage()

private static Message getPostMessage(Runnable r) {
	// Get a new Message using reuse
    Message m = Message.obtain();
	// Save the Runnable to the Message callback
    m.callback = r;
    return m;
}

@UnsupportedAppUsage
private static Message getPostMessage(Runnable r, Object token) {
    Message m = Message.obtain();
    m.obj = token;
    m.callback = r;
    return m;
}
Copy the code

The getPostMessage() method, which uses the reuse mechanism to get the new Message and stores the Runnable in the Message’s callback.

summary

  1. Send a messageDivided intosend-Message,post-Callback.post-CallbackThe bottom layer is also throughsend-MessageSend (willRunnableSave toMessagethecallback).
  2. throughhandlerThe message that is sent will eventually be sentthisMessageandthisHandlerAssociation for usethisHandlerTo deal withthisMessage.
  3. If thisHandlerisasynchronousIs sentAll of the messagesAre allasynchronous.

Distribution of the Message

When the looper.loop () method is turned on and the Next () method of the Looper MessageQueue returns a Message, the Handler’s dispatchMessage() method is called as follows.

Loop->loopOnce()

private static boolean loopOnce(final Looper me,
            final long ident, final int thresholdOverride) {
        Message msg = me.mQueue.next(); // might block.// Call Handler to distribute the messagemsg.target.dispatchMessage(msg); . }Copy the code

Let’s look at the Handler’s dispatchMessage() method.

dispatchMessage()

Handler->dispatchMessage()

public void dispatchMessage(@NonNull Message msg) {
    if(msg.callback ! =null) {
	    / / Callback processing
        handleCallback(msg);
    } else {
  	    / / the Message processing
        if(mCallback ! =null) {
	        // Handler's Callback is not null; it takes precedence.
            if (mCallback.handleMessage(msg)) {
	            // Returns true, indicating that the handler Callback has been processed and no longer needs handleMessage() processing.
                return; }}// Handle with the handleMessage methodhandleMessage(msg); }}Copy the code

The dispatchMessage() method, for sending messages, is divided into Callback processing, Message processing, and Message processing first handler Callback processing, handleMessage() method processing.

Description:

  1. dispatchMessage()Methods forpublicandnotfinal, so it can beoverwrite, in general,Don’t overwriteThis method.

Let’s first look at the handleCallback() method that handles Callback.

handleCallback()

private static void handleCallback(Message message) {
	// Call callback.run() to the runnable.run() method
    message.callback.run();
}
Copy the code

The handleCallback() method is executed directly by calling the Run () method of message’s callback (i.e. Runnable).

Next we’ll look at the Handler.callback class, which handles Message first, and the handleMessage() method, which handles Message second.

Handler Callback class

public interface Callback {
    / * * *@returnTrue if no further processing is required. * /
    boolean handleMessage(@NonNull Message msg);
}
Copy the code

If handler.mcallback has a set value, it takes precedence, and the handleMessage() method returns true, then handleMessage() method processing is no longer required.

handleMessage()

public void handleMessage(@NonNull Message msg) {}Copy the code

The handleMessage() method, for processing messages, can be distinguished by the what value of the Message to implement its own logic.

Description:

  1. handleMessage()Methods forpublicandnotfinal, so it can beoverwrite, in general,overwriteThis method.

summary

  1. distributionMessageIt is throughHandlerthedispatchMessage()methodsDistributed processing.
  2. distributionMessageIt is divided intoTo deal withCallback,To deal withMessage.

2.1. Handle Callback by directly calling the run() method of Callback (i.e. Runnable). 2.2. The handler Callback takes precedence over the handleMessage() method. 3. Both dispatchMessage() and handleMessage() methods can be overridden, usually just the handleMessage() method.

Remove Messages and Callbacks

removeMessages()

public final void removeMessages(int what) {
    mQueue.removeMessages(this, what, null);
}

public final void removeMessages(int what, @Nullable Object object) {
    mQueue.removeMessages(this, what, object);
}
Copy the code

removeCallbacks()

public final void removeCallbacks(@NonNull Runnable r) {
    mQueue.removeMessages(this, r, null);
}

public final void removeCallbacks(@NonNull Runnable r, @Nullable Object token) {
    mQueue.removeMessages(this, r, token);
}
Copy the code

removeCallbacksAndMessages()

public final void removeCallbacksAndMessages(@Nullable Object token) {
    mQueue.removeCallbacksAndMessages(this, token);
}
Copy the code

The removeXX method of MessageQueue is used to remove Callbacks specifying what Message or runnable.

Description:

  1. MessageQueueFor information about removing messages, seeTripartite library -Handler source parsing -MessageQueue- Remove Message.

summary

Whether there are Messages and Callbacks

hasMessages()

public final boolean hasMessages(int what) {
    return mQueue.hasMessages(this, what, null);
}
Copy the code

hasCallbacks()

public final boolean hasCallbacks(@NonNull Runnable r) {
    return mQueue.hasMessages(this, r, null);
}
Copy the code

hasMessagesOrCallbacks()

public final boolean hasMessagesOrCallbacks(a) {
    return mQueue.hasMessages(this);
}
Copy the code

All of the above methods are used to determine whether there are any Callbacks specifying what or runnable by calling MessageQueue’s hasMessages method.

Description:

  1. MessageQueueIs there any information about it? Please check it outTripartite library -Handler source parsing -MessageQueue- Whether there is a Message.

summary

other

getMessageName()

public String getMessageName(@NonNull Message message) {
    if(message.callback ! =null) {
	    // Is a Callback type that returns the class name of this Callback (that is, Runnable).
        return message.callback.getClass().getName();
    }
	// Is of type Message, which returns the hexadecimal value of what for this Message.
    return "0x" + Integer.toHexString(message.what);
}
Copy the code

Gets the string representing the specified Message name. By default, Callback returns the class name of the Callback (i.e., Runnable), and Message returns the hexadecimal of what of the Message.

getLooper()

@NonNull
public final Looper getLooper(a) {
    return mLooper;
}
Copy the code

Gets the Looper object for this Handler

dump()

public final void dump(@NonNull Printer pw, @NonNull String prefix) {
    pw.println(prefix + this + "@" + SystemClock.uptimeMillis());
    if (mLooper == null) {
        pw.println(prefix + "looper uninitialized");
    } else {
        mLooper.dump(pw, prefix + ""); }}Copy the code

Dump the state of Looper for debugging. If Looper is empty, print it, otherwise Looper’s dump() method is called.

Description:

  1. LooperFor an introduction to dump, seeOther stars -.