This is the third day of my participation in Gwen Challenge.
What is IdleHandler?
IdleHandler is a mechanism provided by the Handler mechanism that allows us to perform other tasks when idle during the Looper loop. Take a look at it in the source code:
/** * Callback interface for discovering when a thread is going to block * waiting for more messages. */ 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
IdleHandler is an interface defined in MessageQueue. The implementation class implements queueIdle, which has a Boolean return value. Before we get into the usefulness of this return value, let’s look at a few variables related to IdleHandler:
Again in the MessageQueue class:
@unsupportedAppusage private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>(); Private IdleHandler[] mPendingIdleHandlers;Copy the code
Using the same class, let’s look at a few more methods related to IdleHandler:
Public void addIdleHandler(@nonnull IdleHandler handler) {if (handler == null) {throw new NullPointerException("Can't add a null IdleHandler"); } synchronized (this) { mIdleHandlers.add(handler); }} public void removeIdleHandler(@nonnull IdleHandler handler) {synchronized (this) {synchronized (this) { mIdleHandlers.remove(handler); }}Copy the code
In the addIdleHandler method, an exception is thrown if the idleHandler is empty, otherwise the idleHandler is added to the previously defined collection. So now we know that the IdleHandler that we defined is going to be added to a collection. So how do you use this set to make things happen? Since the IdleHandler is within the scope of the handler, then handle the message should still stars that set of mechanisms, we go to have a look.
Looper first calls the loop() method to open the loop
public static void loop() { final Looper me = myLooper(); . for (;;) {// Call MessageQueue's next method Message MSG = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; }...Copy the code
The loop method will call the next method of MessageQueue, and then go back to MessageQueue to study the next method.
PendingIdleHandlerCount = -1; Message next() {pendingIdleHandlerCount = -1; // -1 only during first iteration for (;;) {//JNI method, if no message processing goes to sleep nativePollOnce(PTR, nextPollTimeoutMillis); synchronized (this) { ... / / thread here that execution going to sleep the if (pendingIdleHandlerCount < 0 && (mMessages = = null | | now < mMessages. When)) {/ / get all the registered number of free news 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)]; Handlers = mIdleHandlers. ToArray (mPendingIdleHandlers); } for (int I = 0; int I = 0; i < pendingIdleHandlerCount; i++) { final IdleHandler idler = mPendingIdleHandlers[i]; mPendingIdleHandlers[i] = null; // release the reference to the handler boolean keep = false; Keep = idler.queueidle (); } catch (Throwable t) { Log.wtf(TAG, "IdleHandler threw exception", t); } // If queueIdle returns false, then check if (! Synchronized (this) {mIdleHandlers. Remove (idler); synchronized (this) {mIdleHandlers. PendingIdleHandlerCount = pendingIdleHandlerCount = 0 so we do not run them again PendingIdleHandlerCount = 0; pendingIdleHandlerCount = 0; . }}Copy the code
A thread in a message loop has nothing to do when its message queue is empty, or when the processing time of messages stored in the header of the message queue is longer than the system’s current time. At this point, the thread is in an idle state and then enters the sleep wait state. However, before entering the sleep wait, the thread will process the idle messages that have been registered.
-
We register idleHandler with the collection mIdleHandlers, and when we iterate through the IdleHandlers we copy the data in the collection to the mPendingIdleHandlers array, and then we iterate through the handlers.
-
The return value of the queueIdle method determines whether the IdleHandler is removed from the mIdleHandlers collection. That is, the queueIdle() method is executed multiple times when it returns true, meaning that the IdleHandler does not remove it from the IdleHandler queue after executing it once, and will continue executing it at the next idle time. Returning false is executed only once
Through the above analysis, you understand the IdleHandler in the message loop process.
Use in source code
We can see that there are so many places where addIdleHandler is called to register IdleHandler, so let’s look at the IdleHandler in ActivityThread
GcIdler, PurgeIdler
// define GCIdleHandler final GcIdler mGcIdler = new GcIdler(); IdleHandler Final PurgeIdler mPurgeIdler = new PurgeIdler(); . final class GcIdler implements MessageQueue.IdleHandler { @Override public final boolean queueIdle() { doGcIfNeeded(); purgePendingResources(); return false; } } final class PurgeIdler implements MessageQueue.IdleHandler { @Override public boolean queueIdle() { purgePendingResources(); return false; }}... @unsupportedappusage void scheduleGcIdler() {if (! mGcIdlerScheduled) { mGcIdlerScheduled = true; Looper.myQueue().addIdleHandler(mGcIdler); } mH.removeMessages(H.GC_WHEN_IDLE); } // Register void schedulePurgeIdler() {if (! mPurgeIdlerScheduled) { mPurgeIdlerScheduled = true; Looper.myQueue().addIdleHandler(mPurgeIdler); } mH.removeMessages(H.PURGE_RESOURCES); }...Copy the code
Take mGcIdler as an example. After receiving GC_WHEN_IDLE messages in ActivityThread H class, scheduleGcIdler is executed to add IdleHandler and register the IdleHandler set.
conclusion
IdleHandler is a mechanism provided by handlers to perform tasks when a message queue is idle. Its execution timing depends on the situation of MessageQueue. If MessageQueue has been waiting for execution messages to be processed, IdleHandler will not get the opportunity to process, which also indicates that the execution timing of IdleHandler is uncontrollable, and it is not suitable for executing some tasks with higher priorities.
When we encounter some scenarios that need to be delayed during development, we can consider using IdleHandler. For example, during the startup process, we can put some initialization tasks with low priority into IdleHandler to improve the startup speed.
IdleHandler can help us achieve a lot of functions, but because it is not controllable, there will be a lot of problems in use, only understand its principle, in order to use it in practice, better.