IdleHandler是在Hanlder空闲时,也就是没有可处理的消息时候,用来处理空闲任务的一种机制。 有什么作用呢?主要是用于提升性能,可以在消息队列空闲时做一些事情,从而不影响到主线程的任务处理。(卑微小弟,你们重要的大哥们先用,我最后再用)。
用法如下:
Looper.myQueue().addIdleHandler(new IdleHandler() { @Override public boolean queueIdle() { //do something return false; } });这里queueIdle方法的返回参数是bool类型,true代表执行一次后不删除,下次进入空闲时还会调用该回掉方法。false代表执行一次后该IdleHandler就会被删除。 源码在MessageQueue类的next方法,其实就是在消息队列里面没有消息的时候会去查询mIdleHandlers队列,mIdleHandlers队列有数据,也就是有IdleHandler就会去处理执行。 还是简单放下源码吧:
Message next() { for (;;) { synchronized (this) { // 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; mPendingIdleHandlers = 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); } } } } }有人可能要问了,这玩意真的有用吗?确实有用,只是你没用到而已。下面举例两个场景
还是看这个next获取消息的方法:
Message next() { for (; ; ) { synchronized (this) { 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 != 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; return msg; } } } } }可以看到一开始就会判断这个消息两个条件:
ok,继续往下看
//MessageQueue.java boolean enqueueMessage(Message msg, long when) { synchronized (this) { msg.markInUse(); msg.when = when; Message p = mMessages; boolean needWake; 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 { // Inserted within the middle of the queue. Usually we don't have to wake // up the event queue unless there is a barrier at the head of the queue // and the message is the earliest asynchronous message in the queue. needWake = mBlocked && p.target == null && msg.isAsynchronous(); 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; } // We can assume mPtr != 0 because mQuitting is false. if (needWake) { nativeWake(mPtr); } } return true; }enqueueMessage`这个方法主要做了两件事:
1、插入消息,msg。通过一个循环,找出msg应该插入的位置(按照时间排序),然后插入msg到mMessages(消息队列)
2、唤醒消息队列。消息队列在没有消息的时候,会阻塞在queue.next()方法这里,所以来了消息就要唤醒线程。这里的阻塞和唤醒主要依靠底层的epoll 机制,具体我也不太懂,有懂得大神可以在评论区留言
既然有了消息,那么Looper那端就要取消息了,怎么取的?就是我们要说的第二个重要方法loop
loop
//Looper.java public static void loop() { final Looper me = myLooper(); final MessageQueue queue = me.mQueue; for (;;) { Message msg = queue.next(); // might block // This must be in a local variable, in case a UI event sets the logger final Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } try { msg.target.dispatchMessage(msg); } catch (Exception exception) { throw exception; } finally { } if (logging != null) { logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); } } } /** * Handle system messages here. */ public void dispatchMessage(@NonNull Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }这里截取了部分代码,可以看到,loop方法通过一个死循环,不断的从MessageQueue获取消息,并且通过msg.target的dispatchMessage方法进行处理,target上文说过也就是消息对应的Handler。 而dispatchMessage方法最后也会调用到handler的handleMessage方法了。至此,流程已走通。
ok,还剩最后一个重要的点没说了。就是到底MessageQueue是怎么取出消息的呢?
代码贴上
//MessageQueue.java Message next() { for (;;) { 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 != 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(); return msg; } } else { // No more messages. nextPollTimeoutMillis = -1; } // 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; mPendingIdleHandlers = 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); } } } } }至此,Handler的大概已经了解的差不多了,是不是觉得Handler太神奇了,你也忍不住想去好好看看它的源码了呢?也许还有一些功能没被利用起来,等着你去发现
有说的不对的地方望指正,谢谢。
面试前的知识梳理,储备提升