Why Handler
Android famously does not allow UI updates in child threads. But what if we need to update the interface data after the child thread completes a time-consuming operation? At this point, we can use handlers for UI updates. Note that to update the UI we need to send a Message to MessageQueue held by the main thread, otherwise the application will still crash.
In addition to updating the UI, Handler is the Messaging mechanism of the Android system. It defines a set of rules for handling messages, broadcasting, services, and communication between threads.
Handler Handler Handler Handler Handler Handler Handler Handler Handler Handler Handler Handler Handler Handler Handler Handler Handler Handler Handler Handler
Handler Sends messages
Handler sends messages in two ways: sendMessage and POST. After reading the source code, the post method actually calls sendMessage. Let’s take a look at the sendMessage flow. By calling sendMessage, you end up in the following method:
MessageQueue must not be empty or the program will throw an exception. Let’s look at enqueueMessage:
Two important processes are done here:
- Msg. target = this,
So the target is the Handler for the sendMessage call.
(Remember the point here) - The enqueueMessage method of MessageQueue is called.
So far, the process has come to MessageQueue. Now look at the enqueueMessage method for MessageQueue.
Iii. Work flow of MessageQueue
Because the enqueueMessage method is quite long, we will not take a screenshot here, but look at the following code :(omit some code)
boolean enqueueMessage(Message msg, long when) {
// 1. Target cannot be empty; otherwise, an exception will be thrown
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
// 2, lock, can not have more than one Handler to send messages
synchronized (this) {
msg.when = when;
Message p = mMessages; // The next MSG to be queued
boolean needWake;
(1) the queue is empty; (2) the queue is empty;
// (2) this MSG needs to be processed immediately, (3) it takes longer to process than the outgoing section
// Point processing time is smaller
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 {
needWake = mBlocked && p.target == null && msg.isAsynchronous();
// select * from head; // select * from head; // select * from head;
// Insert at an appropriate time, or at the end of the list, which is what the for loop does
// Insert the list node
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;
}
Queue.next () will sleep if MSG is not available
if(needWake) { nativeWake(mPtr); }}return true;
}
Copy the code
The explanation is already in the above code, so let’s make a simple generalization:
MessageQueue is essentially a one-way linked list,
The enqueue operation is locked. Multiple MSGS cannot be enqueued at the same time.- When the queue is inserted, the appropriate insertion location is selected according to whether the current queue is empty or the time it takes to process the message.
- Finally, determine whether wake up is required
So far, we’ve looked at how the Handler sends the message and how the message is inserted into the linked list. How is the message handled? The loop() method of Looper is called before the message can be processed.
4. Looper’s working process
Looper’s loop() method is also quite long.
public static void loop(a) {
// 1, get Looper object, set null processing
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
// get MessageQueue
final MessageQueue queue = me.mQueue;
for (;;) {
Call MessageQueue next() and return MSG
Message msg = queue.next(); // might block
if (msg == null) {
return; }...try {
// The target of SendMessage is set. This target is the Handler that calls SendMessage
msg.target.dispatchMessage(msg);
} catch (Exception exception) {
} finally{ } msg.recycleUnchecked(); }}Copy the code
The code itself is long, but it doesn’t actually do much, so here’s a quick summary:
- Looper.prepare() must be called before looper.loop () is called, if not
Looper object will throw an exception.
- Call MessageQueue’s next method to continuously fetch messages from the queue.
- The MSG is then passed to the Handler’s dispatchMessage() for processing.
The source code shows that queue.next() can block, so what does this method do? Also, why call looper.prepare () in the first place, and what does it do? Let’s start with the easy ones:
The looper.prepare () creates a Looper object and implements one and only such Looper object per thread using ThreadLocal. Why create Looper? Can’t we do it without it? Let’s look at the Handler constructor:
As you can see, if Looper is empty, the program throws the exception. This myLooper() is used to get the current thread’s Looper object:
Timing dictates that we call looper.prepare () before new Handler(). Looper.prepare() is not called when the main thread uses Handler.
It turns out that the main() method of ActivityThread already does this for us:
The prepareMainLooper() internally calls looper.prepare (). So far, we have addressed Looper issues and explained why Looper must exist. There’s still one question left. What does the queue.next() method do? Why is it blocking?
Next look at MessageQueue’s next() method :(some code has been omitted)
Message next(a) {
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1;
int nextPollTimeoutMillis = 0;
for (;;) {
if(nextPollTimeoutMillis ! =0) {
Binder.flushPendingCommands();
}
// This is a native method that sleeps if messageQueue has no messages to process
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if(msg ! =null && msg.target == null) {
// 2. The synchronization barrier looks for the next asynchronous message in the queue
do {
prevMsg = msg;
msg = msg.next;
} while(msg ! =null && !msg.isAsynchronous());
}
if(msg ! =null) {
if (now < msg.when) {
// 3) the next MSG to be queued has not yet reached the time limit, and calculates the time needed to block
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// get a MSG that can be processed and return it
mBlocked = false;
if(prevMsg ! =null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
returnmsg; }}else {
// No more messages.
nextPollTimeoutMillis = -1; }... }}}Copy the code
The important points have already been mentioned above. Here’s a summary:
- The process of obtaining MSG may block, and the native Native Pollonce method is invoked
- There is a synchronization barrier when retrieving messages, that is, messages with empty target (Handler) corresponding to MSG are filtered.
- If a MSG is available, the MSG is returned.
Fourth, look at Handler
To sort out what we know now:
- To create a Handler, you must first create the Looper object and then call the looper.loop () method for the Handler to work.
- Sending messages via Handler sendMessage calls queue.enqueueMessage, which is a one-way list. When calling this method, The MSG is inserted into the appropriate position based on the current queue state and when.
- Queue.next () may sleep because the appropriate MSG is not available, and will decide if it needs to wake up when queue.enqueuemessgae is available.
This MSG is sent to the dispatchMessage Handler.
msg.callback
It’s a Runnable object that we passed in via post, and we wouldn’t have gotten there if we hadn’t used POSThandleCallback(msg)
In the.- MCallback is a CallBack object. If this parameter is not passed when we create the Handler, mCallback is null.
- Finally, you get to the handleMessage(MSG).
Additional, because the level is limited, have wrong place unavoidable, rather misdirect others, welcome big guy to point out! Code word is not easy, thank you for your attention! 🙏