Handler延时处理消息的流程

来源:互联网 发布:乾坤一号指标源码 编辑:程序博客网 时间:2024/05/21 07:48

前言

昨天碰到一个关于handler的问题,当我使用sendEmptyMessageDelayed这个方法,在手机熄屏的情况下并没有按时发送消息,而且中间的时间还不固定,有时候短,有时候长,但是在亮屏和充电情况下,没有该问题。
难道handler的延时本身就不准?带着疑问我看了下handler的运行机制,找到了答案!

next()和enqueueMessage()

本篇文章默认为你已经知道handler的使用发法,所以我不会对handler的基础只是做介绍了。

当我使用sendEmptyMessageDelayed的时候,源码依次的调用顺序是;
sendEmptyMessageDelayed(int what, long delayMillis) —>
sendMessageDelayed(Message msg, long delayMillis) —>
sendMessageAtTime(Message msg, long uptimeMillis) —>
enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)

其中enqueueMessage调用的MessageQueue的boolean enqueueMessage(Message msg, long when),接下来我们来看看该函数。

文件:MessageQueue.java

boolean enqueueMessage(Message msg, long when) {        if (msg.target == null) {            throw new IllegalArgumentException("Message must have a target.");        }        if (msg.isInUse()) {            throw new IllegalStateException(msg + " This message is already in use.");        }        synchronized (this) {            //退出时,回收msg,放到消息池            if (mQuitting) {                IllegalStateException e = new IllegalStateException(                        msg.target + " sending message to a Handler on a dead thread");                Log.w(TAG, e.getMessage(), e);                msg.recycle();                return false;            }            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; //这时为true            } else {                // 插入到队列中间,经常我们不需要唤醒事件队列,除非队列的                // 头部有一个障碍或者消息是该队列最早的异步消息                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;    }

从代码可以看出该类的作用是把消息按时间顺序排序,并且控制线程的唤醒,但是我们对mBlocked和handler是怎么记时,还是不清楚,接来我们来看看Loop是怎么取消息的

我们看Loop.loop()就可以知道,取消息是这行代码:

Message msg = queue.next(); // might block

所以我们再看看next()

文件:MessageQueue.java

Message next() {        // Return here if the message loop has already quit and been disposed.        // This can happen if the application tries to restart a looper after quit        // which is not supported.        final long ptr = mPtr;        if (ptr == 0) {            return null;        }        int pendingIdleHandlerCount = -1; // 第一个迭代时为 -1        int nextPollTimeoutMillis = 0; // 阻塞的时间        for (;;) {            if (nextPollTimeoutMillis != 0) {                Binder.flushPendingCommands();            }            // 阻塞操作            nativePollOnce(ptr, nextPollTimeoutMillis);            synchronized (this) {                // 获取系统启动后,到现在的时间                final long now = SystemClock.uptimeMillis();                Message prevMsg = null;                Message msg = mMessages;                if (msg != null && msg.target == null) {                    // 查找下一条异步消息                    do {                        prevMsg = msg;                        msg = msg.next;                    } while (msg != null && !msg.isAsynchronous());                }                if (msg != null) {                    if (now < msg.when) {                        // 如果时间未到,设置下一轮需要等待的时间                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);                    } else {                        // 得到消息                        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 {                    // 没有消息                    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();                }                if (pendingIdleHandlerCount <= 0) {                    // 没有idle handlers 在运行,loop需要等待                    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);                    }                }            }            // 重置idle handler 的数量为0,这样我们就不会再次运行            pendingIdleHandlerCount = 0;            // 当调用一个空闲的handler时, 一个新的消息可以被分发            // 因此可以不需要等待,直接查询pending message            nextPollTimeoutMillis = 0;        }    }

从这个方法中可以看出,handler获取时间的方式是调用SystemClock.uptimeMillis(),并用它和消息的里包含的时间进行对比。
同时next()方法内部如果有阻塞,会把mBlocked设置true,在下一个Message进队列时会判断这个message的位置,如果在队首就会调用nativeWake()方法唤醒线程。

综上所述:
1. 如果我sendEmptyMessageDelayed发送了消息A,延时为500ms,这时消息进入队列,触发了nativePollOnce,Looper阻塞,等待下一个消息,或者是Delayed时间结束,自动唤醒;
2. 在1的前提下,紧接着又sendEmptyMessage了消息B,消息进入队列,但这时A的阻塞时间还没有到,于是把B插入到A的前面,然后调用nativeWake()方法唤醒线程
3. 唤醒之后,会重新都取队列,这是B在A前面,有不需要等待,于是直接返回给Looper
4. Looper处理完该消息后,会再次调用next()方法,如果发现now大于msg.when则返回A消息,否则计算下一次该等待的时间

看到这里你可能想说,这和我的问题没什么关系啊,别急,答案就在SystemClock.uptimeMillis(),handler是通过它来获取时间的,但uptimeMillis()是不包括休眠的时间的,所以手机如果在休眠状态下(android 7.0在灭屏情况下很容易进入休眠),那时间就一直不变,至于中途又发送消息了,那是因为手机被唤醒了(android7.0会定时唤醒手机,接收消息),这时执行完delay操作,就可以发送消息了。

0 0
原创粉丝点击