This is the fourth day of my participation in the November Gwen Challenge. Check out the details: The last Gwen Challenge 2021
Handler
Handler is the object associated with the thread MessageQueue queue to send and process messages and Runnable.
1. Send Runnable as follows:
- post(Runnable)
- postAtTime(Runnable, long)
- postDelayed(Runnable, long)
2. Send a Message as follows:
- sendEmptyMessage(int)
- sendMessage(Message)
- sendMessageAtTime(Message, long)
- sendMessageDelayed(Message, long)
Note that the handler sending the Runnable object method actually calls the SendMessage method. To view the source code directly, Runnable is used as an input parameter to getPostMessage, which is converted to the Message object, and Runnable is assigned to the Message callback.
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
Copy the code
3.dispatchMessage
Handler’s handleMessage is invoked via dispatchMessage. The handler receives messages in three ways.
public void dispatchMessage(Message msg) { if (msg.callback ! = null) { handleCallback(msg); } else { if (mCallback ! = null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); }}Copy the code
- if
msg
thecallBack
Not null, callMessage
theRunnable
The run method
private static void handleCallback(Message message) {
message.callback.run();
}
Copy the code
- if
Handler
themCallback
The interface callback is not null
public interface Callback {
public boolean handleMessage(Message msg);
}
Copy the code
- The last call
handleMessage
/**
* Subclasses must implement this to receive messages.
*/
public void handleMessage(Message msg) {
}
Copy the code
4. To receive the Message
- handleMessage(Message)
So there is no receiving Runnable method, which explains why the sent Runnable is converted to Message.
5. Attention!!
The mLooper required to create a Handler in a child thread cannot be null. This should all be clear: The Handler constructor checks whether the mLooper is null and throws an exception if it is. Looper is created by calling looper.prepare () and instantiating Looper MessageQueue and Thread before creating Handler in the child Thread.
Looper
Looper is the class used to run message queues in Threads. Thread is not associated with Looper by default. 1.prepare
Call prepare in the thread to run the loop. Sthreadlocal.get () gets Looper, or new Looper() if null.
public static void prepare() { prepare(true); } 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)); }Copy the code
2.loop
Let Looper loop through Meessage in MessageQueue. The Loop method is long and basically has for (;;) in it. Thread blocking keeps sending messages from the queue to the Hanlder for processing. Specific in MSG. Target. DispatchMessage (MSG). MSG. Target object is the Hanlder. Look at this
for (;;) { Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; }... Omit part of the code final long slowDispatchThresholdMs = me. MSlowDispatchThresholdMs; final long traceTag = me.mTraceTag; if (traceTag ! = 0 && Trace.isTagEnabled(traceTag)) { Trace.traceBegin(traceTag, msg.target.getTraceName(msg)); } final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis(); final long end; try { msg.target.dispatchMessage(msg); end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis(); } finally { if (traceTag ! = 0) { Trace.traceEnd(traceTag); }}... Msg.recycleunchecked (); }Copy the code
3.quit
Looper calls the loop method to go into an infinite loop, so how can Looper stop polling MessageQueue? Looper provides quit and quitSafely to quit Looper.
- quit
Quit is insecure because some messages may exist before the Looer ends and cannot be distributed in the MessageQueue. *
Using this method may be unsafe because some messages may not be delivered before the looper terminates. Consider using {@link #quitSafely} instead to ensure that all pending work is completed in an orderly manner.
- quitSafely
Quit is not safe, so we use the quitSafely. Ensure that messages in MessageQueue are distributed in a timely manner. However, delayed messages cannot be sent after quit. Looper calls MessageQueue’s quit.
public void quit() {
mQueue.quit(false);
}
public void quitSafely() {
mQueue.quit(true);
}
Copy the code
MessageQueue
Now, MessageQueue. So let’s call Looper to quit before we start. MessageQueue quit as long as said removeAllFutureMessagesLocked and removeAllMessagesLocked safe control. 1.quit
void quit(boolean safe) { if (! mQuitAllowed) { throw new IllegalStateException("Main thread not allowed to quit."); } synchronized (this) { if (mQuitting) { return; } mQuitting = true; if (safe) { removeAllFutureMessagesLocked(); } else { removeAllMessagesLocked(); } // We can assume mPtr ! = 0 because mQuitting was previously false. nativeWake(mPtr); }}Copy the code
- when
Looper
callquit
Unsafe exitremoveAllMessagesLocked
, the loopMessageQueue
Will exit the previous queueMessage
Clear them all.
private void removeAllMessagesLocked() { Message p = mMessages; while (p ! = null) { Message n = p.next; p.recycleUnchecked(); p = n; } mMessages = null; }Copy the code
- when
Looper
callquitSafely
performremoveAllFutureMessagesLocked
, get the currentmMessages
thewhen
andSystemClock.uptimeMillis()
To obtain theboot
Milliseconds from the beginning to the present. ifMessage
More time thanquit
Time to perform an unsafe exitquit
Under theremoveAllMessagesLocked
. Otherwise a loop will be enteredMessageQueue
To perform theMessage
Hand it out untilMessageQueue
Empty or time out (when > now).
Ps: So the delay in sending a Message or Runnable by the handler comes into play here.
private void removeAllFutureMessagesLocked() {
final long now = SystemClock.uptimeMillis();
Message p = mMessages;
if (p != null) {
if (p.when > now) {
removeAllMessagesLocked();
} else {
Message n;
for (;;) {
n = p.next;
if (n == null) {
return;
}
if (n.when > now) {
break;
}
p = n;
}
p.next = null;
do {
p = n;
n = p.next;
p.recycleUnchecked();
} while (n != null);
}
}
}
Copy the code
2.enqueueMessage
We saw earlier that when we send a Message, we end up executing the enqueueMessage method of a queue.
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
Copy the code
MessageQueue does not arrange messages in an array; MessageQueue only holds the current mMessages. A new Message added to the queue determines its position in the queue based on its WHEN. You can also see that if the Thread is dead, an illegal exception is thrown and the sent Message is reclaimed.
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."); } synchronized (this) { if (mQuitting) { 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; Message p = mMessages; boolean needWake; if (p == null || when == 0 || when < p.when) { // New head, wake up the event queue if blocked. msg.next = p; mMessages = msg; needWake = mBlocked; } else { // Inserted within the middle of the queue. Usually we don't have to wake // up the event queue unless there is a barrier at the head of the queue // and the message is the earliest asynchronous message in the queue. needWake = mBlocked && p.target == null && msg.isAsynchronous(); Message prev; for (;;) { prev = p; p = p.next; if (p == null || when < p.when) { break; } if (needWake && p.isAsynchronous()) { needWake = false; } } msg.next = p; // invariant: p == prev.next prev.next = msg; } // We can assume mPtr ! = 0 because mQuitting is false. if (needWake) { nativeWake(mPtr); } } return true; }Copy the code
Message
And finally, Message. The main Message member parameters are: long WHEN, Bundle data, Handler target, Runnable callback, and Message Next. You can see that Message has no precursor and only one successor. So MessageQueue only stores the current mMessages and can find the next Message. 1.Runnable Message has the member parameter Runnable. Remember that the Handler Post was a Runnable and then converted to a Message, and the Runnable that was the input parameter was there. If the Runnable of the Message is not empty, the dispathMessage of the Handler will go this way. Handler is Post, and Runnable is run when received.
The last
The whole point is to figure out Handler, Looper, MessageQueue, and Message. The Handler acts as the receiver and sender of the Message and inserts the Message into the MessageQueue. Looper acts as a Handler, picking up messages from MessageQueue and dropping them to handlers. So why does Handler, as Message receiver and sender, go all the way around Queue and Looper? Thread is also involved. Since the topic is mainly about the Handler mechanism, Thread will be covered separately next time. Handler is used to solve interprocess communication problems and handle events asynchronously, creating child threads to do asynchronous operations, and returning the desired content to the main thread to achieve the purpose of not blocking the UI thread.
class LooperThread extends Thread { public Handler mHandler; public void run() { Looper.prepare(); mHandler = new Handler() { public void handleMessage(Message msg) { // process incoming messages here } }; Looper.loop(); }}Copy the code
Finish with a picture of the Handler mechanism in action that is easier to understand. The source of the image is linked at the end of this article
reference
- Developer.android.com/reference/a…
- Developer.android.com/reference/a…
- Android.xsoftlab.net/reference/a…
- Blog.mindorks.com/android-cor…