Interviewer: "Look familiar Handler mechanism write on your resume, and that talk IdleHandler, right?"

Scratch. Introduction

Android Handler mechanism considered basic skills, interview regulars. But now the interview, the majority would not have let you talk directly Handler mechanism of how Looper is cyclical, how to manage MessageQueue Message, etc., but on the scene to ask questions and see if you master the mechanisms of whether Handler solid.

In this paper, to talk about Handler in IdleHandler, that we are less used functions. It can do? how to use? What are the appropriate usage scenarios? What is not appropriate usage scenarios? There are areas in the Android Framework uses it in?

Two. IdleHandler

2.1 Handler briefly mechanism

Before saying IdleHandler, briefly look at Handler mechanism.

Handler is a standard event-driven model, the presence of a message queue MessageQueue, which is a priority queue based on the message time to trigger, as well as a message queue based on this event loop Looper, Looper by the circulation, to be treated continuously withdrawn from the MessageQueue the Message, and then by the corresponding event handler Handler / callback to handle.

MessageQueue which is managed Looper, Looper sync MessageQueue is created in the construction and use of this ThreadLocal TLS, bind it with the current thread. The main thread App at startup, has been constructed and is ready Looper object in the main thread, developers only need to be directly used.

Handler class encapsulates most of the "Handler mechanism" external user interface, the method by which the send / post related to the message queue MessageQueue insert a Message. Looper in the cycle, the process will continue to be taken from a MessageQueue the Message to be treated.

IdleHandler associated logic used, it takes a message MessageQueue the next()process.

What 2.2 IdleHandler that? how to use?

IdleHandler put it plainly, is a mechanism provided by the Handler, Looper can in the course of the event cycle, when there is idle, a mechanism that allows us to perform tasks.

IdleHandler MessageQueue is defined, which is an interface.

// MessageQueue.java
public static interface IdleHandler {
  boolean queueIdle();
}
复制代码

You can see, you need to realize that when defining queueIdle()method. Simultaneously with return value of true indicates a persistent IdleHandler repeated use, return false representation is a disposable IdleHandler.

Since IdleHandler is defined in the MessageQueue using it also needs the MessageQueue. It defines the corresponding add and remove the MessageQueue methods.

// MessageQueue.java
public void addIdleHandler(@NonNull IdleHandler handler) {
	// ...
  synchronized (this) {
    mIdleHandlers.add(handler);
  }
}
public void removeIdleHandler(@NonNull IdleHandler handler) {
  synchronized (this) {
    mIdleHandlers.remove(handler);
  }
}
复制代码

In fact, you can see the add or remove operations are mIdleHandlers, it is a type ArrayList.

When idle since IdleHandler mainly occur in the MessageQueue is performed, when idle appear?

MessageQueue is a priority-based message queue trigger time, it appears there is an empty queue two scenarios.

  1. MessageQueue is empty, no message;
  2. MessageQueue recent message to be treated, a delay message (when> currentTime), performed with hysteresis;

Both scenarios will attempt to execute IdleHandler.

IdleHandler scene processing, just Message.next()the method that gets the message queue under a pending message, we now look at specific logic.

Message next() {
	// ...
  int pendingIdleHandlerCount = -1; 
  int nextPollTimeoutMillis = 0;
  for (;;) {
    nativePollOnce(ptr, nextPollTimeoutMillis);

    synchronized (this) {
      // ...
      if (msg != null) {
        if (now < msg.when) {
          // 计算休眠的时间
          nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
        } else {
          // Other code
          // 找到消息处理后返回
          return msg;
        }
      } else {
        // 没有更多的消息
        nextPollTimeoutMillis = -1;
      }
      
      if (pendingIdleHandlerCount < 0
          && (mMessages == null || now < mMessages.when)) {
        pendingIdleHandlerCount = mIdleHandlers.size();
      }
      if (pendingIdleHandlerCount <= 0) {
        mBlocked = true;
        continue;
      }

      if (mPendingIdleHandlers == null) {
        mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
      }
      mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
    }

    for (int i = 0; i < pendingIdleHandlerCount; i++) {
      final IdleHandler idler = mPendingIdleHandlers[i];
      mPendingIdleHandlers[i] = null; 

      boolean keep = false;
      try {
        keep = idler.queueIdle();
      } catch (Throwable t) {
        Log.wtf(TAG, "IdleHandler threw exception", t);
      }

      if (!keep) {
        synchronized (this) {
          mIdleHandlers.remove(idler);
        }
      }
    }

    pendingIdleHandlerCount = 0;
    nextPollTimeoutMillis = 0;
  }
}
复制代码

We first explain next()the main logic executed on IdleHandler:

  1. When ready to perform IdleHandler, described the current message to be executed is null, or the execution time of the message has not expired;
  2. As pendingIdleHandlerCount < 0when, according to mIdleHandlers.size()assign pendingIdleHandlerCount, it is the foundation of the late cycle;
  3. The mIdleHandlersin IdleHandler copied to the mPendingIdleHandlersarray, the array is temporary, after entering the for loop;
  4. IdleHandler removed from the loop array and its call queueIdle()record to the return value stored keepin;
  5. When keepis false, the mIdleHandlerremoval of the current cycle IdleHandler, otherwise retained;

IdleHandler mechanism can be seen, the core is in next()when the queue is idle, IdleHandler target loop mIdleHandler recorded, if it queueIdle()returns a value of falsetime, from which mIdleHanderto remove the.

It should be noted that, for mIdleHandlerall operations of this List are to ensure the security thread through synchronized, without worrying about it.

2.3 IdleHander is how to ensure that does not enter an infinite loop?

When a queue is idle, it will survive a loop mIdleHandlersarray and execution IdleHandler.queueIdle()method. And if there are some IdleHander array of queueIdle()returns true, they will remain in the mIdleHandersarray, and then next time will still perform it again.

Note now code logic is still MessageQueue.next()circulating, under this scenario IdleHandler mechanism is how to ensure not enter an infinite loop?

Some articles say IdleHandler not infinite loop, because the next time the cycle is called the nativePollOnce()aid of epoll mechanism into hibernation, next time there is a new message into the team when it will re-awaken, but this is wrong.

Note the preceding next()code, resets at the end of the method and pendingIdleHandlerCount nextPollTimeoutMillis.

Message next() {
	// ...
  int pendingIdleHandlerCount = -1; 
  int nextPollTimeoutMillis = 0;
  for (;;) {
		nativePollOnce(ptr, nextPollTimeoutMillis);
    // ...
    // 循环执行 mIdleHandlers
    // ...
    pendingIdleHandlerCount = 0;
    nextPollTimeoutMillis = 0;
  }
}
复制代码

nextPollTimeoutMillis decided to enter the next nativePollOnce()time timeout, it passes equal to 0 will not go to sleep, so that natievPollOnce()goes to sleep so it will not infinite loop is wrong.

This is well understood, after all, IdleHandler.queueIdle()the main thread, the time it performs is not controllable, the message in the case of MessageQueue may change, so the need to deal with it again.

The actual key is not infinite loop that pendingIdleHandlerCount, we look at the code.

Message next() {
	// ...
  // Step 1
  int pendingIdleHandlerCount = -1; 
  int nextPollTimeoutMillis = 0;
  for (;;) {
    nativePollOnce(ptr, nextPollTimeoutMillis);

    synchronized (this) {
      // ...
      // Step 2
      if (pendingIdleHandlerCount < 0
          && (mMessages == null || now < mMessages.when)) {
        pendingIdleHandlerCount = mIdleHandlers.size();
      }
     	// Step 3
      if (pendingIdleHandlerCount <= 0) {
        mBlocked = true;
        continue;
      }
      // ...
    }
		// Step 4
    pendingIdleHandlerCount = 0;
    nextPollTimeoutMillis = 0;
  }
}
复制代码

We comb:

  • Step 1, before the cycle begins, pendingIdleHandlerCountan initial value of -1;
  • Step 2, in pendingIdleHandlerCount<0time, will be by mIdleHandlers.size()assignment. That is only the first cycle will change pendingIdleHandlerCountin value;
  • Step 3, if pendingIdleHandlerCount<=0when the loop Continus;
  • Step 4, reset pendingIdleHandlerCountto 0;

In the second cycle, pendingIdleHandlerCountequal to 0, 2 does not change its value Step, then continue to the next iteration in Step 3 will be directly continus, at this time there is no opportunity to modify nextPollTimeoutMillis.

So nextPollTimeoutMillisthere are two possibilities: -1 or wait for the next wake-up interval, the execution to nativePollOnce()when it will go to sleep, waiting to be awakened again.

The next time you wake up, mMessagethere must be a Message to be executed, then MessageQueue.next()the return Looper.loop()of circulation, distribution deal with this Message, followed by another round of new next()go cycling.

2.4 framework how to use IdleHander?

Here basically clear IdleHandler how well some of the details, let's take a look in the system, which will be used IdleHandler local mechanism.

Search IdleHandler in the AS.

Simple explanation:

  1. ActivityThread.Idler in the ActivityThread.handleResumeActivity()call.
  2. ActivityThread.GcIdler is insufficient memory, forced GC;
  3. Instrumentation.ActivityGoing added before Activity onCreate () execution;
  4. Instrumentation.Idler call time is more and more, is related to the call keyboard;
  5. TextToSpeechService.SynthThread transmits a broadcast TTS after completion of the synthesis;

Interested can catch yourself at the source, these scenes are used, the specific use IdleHander do, is to look at the business.

III. Some interview questions

Here we will make it clear IdleHandler do? how to use? What is the problem? And explain the use of some principles.

Here are some basic questions to prepare for your understanding.

Q: IdleHandler what's the use?

  1. IdleHandler Handler is provided in the one kind of timing when the message queue is idle, performing a task;
  2. When a message MessageQueue not currently need to be addressed immediately, it will perform IdleHandler;

Q: MessageQueue provides add remove IdleHandler way / Do I need to be used in pairs?

  1. It is not required;
  2. IdleHandler.queueIdle () return value may be added to remove the MessageQueue IdleHandler;

Q: When mIdleHanders has been not empty, why not enter an infinite loop?

  1. Only -1, will try to perform mIdleHander in pendingIdleHandlerCount is;
  2. When pendingIdlehanderCount next (initially) -1, 0 is executed again after the set, so it will not repeatedly performed;

Q: Can non-essential services to start, move to IdleHandler in to deal with?

  1. not recommend;
  2. IdleHandler processing timing uncontrollable, if MessageQueue message processing has to be, then the implementation of IdleHander opportunities are on the list;

Q: IdleHandler of queueIdle () run in that thread?

  1. Stuck problem, queueIdle running threads (), and only where the current Looper thread MessageQueue relevant;
  2. Like the child thread can be constructed Looper, and add IdleHandler;

III. Summary moment

IdleHandler put to use here and say the principle is cleared.

IdleHandler Handler is one kind of opportunity provided when the message queue is idle, to perform tasks. But the timing of the implementation of its message queue rely case, then if the MessageQueue message has to be performed, IdleHandler has been the lack of implementation, that is, its execution timing is not controllable, unfit to perform some of the more high requirements on the timing of task.

This article on here, there is help for you? Have any questions please leave a message . Do not forget that there is help forward, good-looking point , thank you!


Recommended reading:

No public reply to grow back, " growth ", I will be prepared learning materials.

Guess you like

Origin juejin.im/post/5e4de2f2f265da572d12b78f