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操作,就可以发送消息了。
- Handler延时处理消息的流程
- 关于Handler的延时发送消息
- Handler 的处理消息
- Handler的消息处理
- Android消息处理流程(handler)
- Handler处理消息的顺序
- Handler处理消息的机制
- handler looper 消息发送和消息处理流程
- Android Handler 实例化-->发送消息-->处理消息流程
- 消息的处理流程
- 延时处理(Timer/TimerTask,Handler)
- DELPHI消息的处理流程
- DELPHI消息处理的流程
- zigbee消息的处理流程
- handler 与 消息的处理 简便方法
- 一个activity中多个handler的消息处理
- Android异步消息处理Handler的使用
- Handler的使用与异步消息处理
- 继承体系知识总结
- 《从零开始搭建游戏服务器》Netty导入创建一个Socket服务器
- 应该怎样学数据结构
- vtkMergePoints 插入点时去除重复点
- 75篇关于Tomcat源码和机制的文章
- Handler延时处理消息的流程
- Git仓库分支(Branch)和标签(Tag)
- 制作CAB包
- 驱动调试的一些想法
- 安卓实现支付宝6位密码输入界面
- JAVA必备基础知识点
- mysql性能调优_修改文件my.ini
- jms使用长连接
- python基础