introduce

IdleHandler is an interface defined in MessageQueue that is generally used for performance tuning. When there is no message in the message queue that needs to be executed immediately, the queueIdle method of the IdleHandler is actively fired. Return false, that is, execute only once; The return value is true, meaning that this method is fired every time there is no message in the message queue that needs to be executed immediately.

public final class MessageQueue { public static interface IdleHandler { boolean queueIdle(); }}Copy the code

use

Register the listener by retrieving the MessageQueue queue corresponding to looper.

Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
    @Override
    public boolean queueIdle() {
        // doSomething()
        return false;
    }
});
Copy the code

The source code parsing

The IdleHandler execution source code is very short.

Message next() {// Hide extraneous code... int pendingIdleHandlerCount = -1; // -1 only during first iteration int nextPollTimeoutMillis = 0; for (; ;) {// hide irrelevant code... // If first time idle, then get the number of idlers to run. // Idle handles only run if the queue is empty or if the first message // in the queue (possibly a barrier) is due to be handled in the future. if (pendingIdleHandlerCount < 0 && (mMessages == null || now < mMessages.when)) { pendingIdleHandlerCount = mIdleHandlers.size(); } if (pendingIdleHandlerCount <= 0) { // No idle handlers to run. Loop and wait some more. mBlocked = true; continue; } if (mPendingIdleHandlers == null) { mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)]; } mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers); } // Run the idle handlers. // We only ever reach this code block during the first iteration. 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 { keep = idler.queueIdle(); } catch (Throwable t) { Log.wtf(TAG, "IdleHandler threw exception", t); } if (! keep) { synchronized (this) { mIdleHandlers.remove(idler); } } } // 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
  1. inMessageQueuenextmethodsforIn an infinite loop, getmIdleHandlersThe number ofpendingIdleHandlerCount;
  2. throughmMessages == null || now < mMessages.whenIf the current message queue is empty or no message needs to be executed, thependingIdleHandlerCountThe assignment;
  3. When the number is greater than zero, we iterate over and getIdleHandler, the implementation ofqueueIdle()
  4. The return value isfalseWhen, actively remove listeningmIdleHandlers.remove(idler) ;

Usage scenarios

  1. If you startActivity,Fragment,DialogContains a large amount of data and view loading, resulting in the first open animation switch stuck or a moment of white screen, can be part of the loading logic intoqueueIdle()In the processing. For example, the loading of the bootmap and popup prompt;
  2. System source codeActivityThreadGcIdlerGC is attempted in some scenarios while waiting for message queues to be temporarily idle;
  3. System source codeActivityThreadIdlerIn thehandleResumeActivity()Methods are registeredIdler()And wait forhandleResumeActivityCall when the rear view is drawn and the message queue is temporarily idleAMSactivityIdleMethod to check the life cycle status of the page, triggeredactivitystopLife cycle, etc. That’s why weBActivityjumpCActivityWhen,BActivitylife-cycleonStop()Will be inCActivityonResume()After.
  4. Some third-party frameworksGlideLeakCanaryAnd so onIdleHandler, interested friends can look at the source;