Handler中的IdleHandler

作者: 糖葫芦_倩倩 | 来源:发表于2019-03-07 13:59 被阅读14次

    1.1 IdleHandler 基本情况

    IdleHandler 可以用来提升性能,主要用在我们希望能够在当前线程 消息队列空闲时 做些事情(例如UI线程在显示完成后,如果线程空闲我们就可以提前准备其他内容)的情况下,不过最好不要做耗时操作。

    IdleHandler 位于 MessageQueue 类中的一个静态接口,如下:

    MessageQueue#IdleHandler
    class MessageQueue{
        ...
        /**
         * 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.
             */
             //返回true就是单次回调后不删除,下次进入空闲时继续回调该方法,false 只回调单次执行完之后会移除
            boolean queueIdle();
        }
        
       //判断当前队列是不是空闲的,辅助方法
       public boolean isIdle() {
            synchronized (this) {
                final long now = SystemClock.uptimeMillis();
                return mMessages == null || now < mMessages.when;
            }
        }
    
      //添加一个IdleHandler 到空闲队列中,ArrayList 存储
       public void addIdleHandler(@NonNull IdleHandler handler) {
            if (handler == null) {
                throw new NullPointerException("Can't add a null IdleHandler");
            }
            synchronized (this) {
                mIdleHandlers.add(handler);
            }
        }
        // 删除一个IdleHandler 
        public void removeIdleHandler(@NonNull IdleHandler handler) {
            synchronized (this) {
                mIdleHandlers.remove(handler);
            }
        }
    
        //message 的获取下一条消息
        Message next() {
            ...
            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 != 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;
                    }
    
                    // Process the quit message now that all pending messages have been handled.
                    if (mQuitting) {
                        dispose();
                        return null;
                    }
    
                    // 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();
                    }
                    //表示没有设置idle handler 去运行,就阻塞
                    if (pendingIdleHandlerCount <= 0) {
                        // No idle handlers to run.  Loop and wait some more.
                        mBlocked = true;
                        continue;
                    }
                    //设置长度,最小长度是4
                    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);
                    }
                    //这里看到了吧,如果是返回false, 那就进去了,执行操作后就从集合中remove了
                    if (!keep) {
                        synchronized (this) {
                            mIdleHandlers.remove(idler);
                        }
                    }
                }
            ....
            }
        }
        ...
    }
    

    1.2 使用场景

    Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
                @Override
                public boolean queueIdle() {
                    //这里做一些操作
                    Log.i("xx","addIdleHandler invoked..."+iv.getWidth() + "::"+iv.getHeight());
    
                    return false;
                }
    });
    

    我记得之前我们想要获取xml中某个控件的widthheight 时,在 onCreate 方法中直接获取,会获取到是 0 , 因为这个 view 还未绘制完成,所以获取不到,当时的解决方案我记得是使用 Handler 发送一个延时消息获取,现在有更好的方式实现了,那就是通过 IdleHandler, 如上面代码所示。 当然还可以做一些其它预处理的简单操作。

    IdleHandler 你明白了吗?欢迎大家留言讨论。

    相关文章

      网友评论

        本文标题:Handler中的IdleHandler

        本文链接:https://www.haomeiwen.com/subject/czscpqtx.html