preface
As one of the most important parts of the system. Handler is a must-ask in an interview. This article just introduces a little IdleHandler from Handler. For additional details about Handler, refer to the interview questions I wrote about analyzing Handler from source code
What is IdleHandler
The following interface can be found in the Handler source code. The annotation section explains its function well. A simple overview
The idleHandler task is executed when there are no immediate messages in the message queue
public static interface IdleHandler {
/** * Called when the message queue has run out of messages and will now * wait for more. Return true to keep your idle handler active, false * to have it removed. This may be called if there are still messages * pending in the queue, but they are all scheduled to be dispatched * after the current time. */
boolean queueIdle();
}
Copy the code
How to use
Handler provides two methods
public void addIdleHandler(@NonNull IdleHandler handler) {
if (handler == null) {
throw new NullPointerException("Can't add a null IdleHandler");
}
synchronized (this) { mIdleHandlers.add(handler); }}Copy the code
public void removeIdleHandler(@NonNull IdleHandler handler) {
synchronized (this) { mIdleHandlers.remove(handler); }}Copy the code
It is also very simple to use
Looper.myQueue().addIdleHandler{
Log.i(TAG, "this is meeage")
false
}
Copy the code
Source code analysis
Start with addIdleHandler. Add IdleHandler to mIdleHandlers
The mIdleHandlers are an array. Used to store idle tasks
private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
Copy the code
We all know that the Handler pulls the message out of the MessageQueue. Let’s look at how the IdleHandler performs this task when there is no immediate message.
Message next() {
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
// 👇 The default number of idle tasks is -1
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if(nextPollTimeoutMillis ! =0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if(msg ! =null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while(msg ! =null && !msg.isAsynchronous());
}
// 👇 if MSG is null There are no messages in the queue. This is called the idle state
if(msg ! =null) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
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();
// 👇 Returns idle tasks when there are tasks
returnmsg; }}else {
// No more messages.
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
// 👇 pendingIdleHandlerCount The default is -1
if (pendingIdleHandlerCount < 0
// When there is no current message
&& (mMessages == null || now < mMessages.when)) {
// Record the number of idle tasks we saved
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
// 👇 default null creates an array for idle tasks
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// 👇 Execute idle tasks
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
try {
👇 idle interface has a return value.
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
👇 executes only once when the return value is false. The loop is executed when true is returned
if(! keep) { synchronized (this) {
// 👇 If false is returned, the idle task is deletedmIdleHandlers.remove(idler); }}}// 👇 Resets the number of idle tasks
// Reset the idle handler count to 0 so we do not run them again.
pendingIdleHandlerCount = 0;
// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
nextPollTimeoutMillis = 0; }}Copy the code