当前位置: 首页 > news >正文

网站建设招标评分表制造企业网站建设

网站建设招标评分表,制造企业网站建设,建设公司网站费用怎么做账,浙江平安建设信息系统网站Handler postDelayed的实现原理 问题描述 Handler.postDelayed()的原理是如何保证延时执行的? 扩展:这样实现的好处是什么? 题目分析 猜测一下 以我们对Handler的了解,内部使用了Looper对消息队列进行循环获取执行&#xff0…

Handler postDelayed的实现原理

问题描述

Handler.postDelayed()的原理是如何保证延时执行的?
扩展:这样实现的好处是什么?

题目分析

猜测一下

以我们对Handler的了解,内部使用了Looper对消息队列进行循环获取执行,所以我们估计postDelayed()是Handler内部搞了一个定时器,
定时器到了delayed的时间就把消息加入到消息队列中,让looper在循环获取到该消息并执行。

真的是这样吗?如果不是,为什么?

我们来追溯一下源码

消息是怎样入队的?

首先调用的是sendMessageDelayed方法


public final boolean postDelayed(@NonNull Runnable r, long delayMillis) {return sendMessageDelayed(getPostMessage(r), delayMillis);}
sendMessageDelayed计算了消息执行的准确时间,当前时间加上延时时间public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {if (delayMillis < 0) {delayMillis = 0;}return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);}public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {MessageQueue queue = mQueue;if (queue == null) {RuntimeException e = new RuntimeException(this + " sendMessageAtTime() called with no mQueue");Log.w("Looper", e.getMessage(), e);return false;}return enqueueMessage(queue, msg, uptimeMillis);}private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,long uptimeMillis) {msg.target = this;msg.workSourceUid = ThreadLocalWorkSource.getUid();if (mAsynchronous) {msg.setAsynchronous(true);}return queue.enqueueMessage(msg, uptimeMillis);}  
Message的enqueueMessage方法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) {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;} 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.nextprev.next = msg;}// We can assume mPtr != 0 because mQuitting is false.if (needWake) {nativeWake(mPtr);}}return true;}

查看源码我们发现:并不是像我们推测的那样使用定时器加入队列,而是简单计算了消息开始执行的时间之后就加入队列了。
MessageQueue中Message的结构就是一个简单的单向链表,只保存了链表头部的引用。
我们分析一下入队过程

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 {···}

如果链表头为空或者延时时间已经到了,则放到列表头,唤醒阻塞队列

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.nextprev.next = msg;

否则遍历链表,安装when的时间顺序插入消息,注意when = SystemClock.uptimeMillis() + delayMillis

Looper是如何出来延时消息的?

我们看看Looper的loop()方法

for (;;) {Message msg = queue.next(); // might blockif (msg == null) {// No message indicates that the message queue is quitting.return;}...

原来是调用了MessageQueue的next方法,注释说明会阻塞。

我们看下MessageQueue的next方法里面做了啥?


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 only during first iterationint 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();}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[i];mPendingIdleHandlers[i] = null; // release the reference to the handlerboolean keep = false;try {keep = idler.queueIdle();} catch (Throwable t) {Log.wtf(TAG, "IdleHandler threw exception", t);}if (!keep) {synchronized (this) {mIdleHandlers.remove(idler);}}}// Reset the idle handler count to 0 so we do not run them again.pendingIdleHandlerCount = 0;// While calling an idle handler, a new message could have been delivered// so go back and look again for a pending message without waiting.nextPollTimeoutMillis = 0;}}

原来在next方法中对链表头部的Message的执行时间进行了判断,如果当前时间小于msg.when,则计算阻塞时间,
然后在循环开始的时候判断如果这个Message有延迟,就调用nativePollOnce(ptr, nextPollTimeoutMillis)进行阻塞。
有兴趣的童鞋可以下Android源码看看native层的nativePollOnce是如何实现的,作用与object.wait()类似,只不过是使用了Native的方法对这个线程精确时间的唤醒。
唤醒之后loop()就能拿到对应的message了。

参考答案

1、比如postDelay()一个延时10秒钟的Runnable A、消息进队,MessageQueue调用nativePollOnce()阻塞,Looper阻塞;

2、紧接着post()一个Runnable B、消息进队,判断现在A时间还没到、正在阻塞,把B插入消息队列的头部(A的前面),然后调用nativeWake()方法唤醒线程;

3、MessageQueue.next()方法被唤醒后,重新开始读取消息链表,第一个消息B无延时,直接返回给Looper;

4、Looper处理完这个消息再次调用next()方法,MessageQueue继续读取消息链表,第二个消息A还没到时间,计算一下剩余时间(假如还剩9秒)继续调用nativePollOnce()阻塞;
直到阻塞时间到或者下一次有Message进队再次唤醒;

比我们的猜测好在哪里?

1、如果用我们的猜测方案,我们每添加一个延时消息就需要维护一个定时器,如果消息多耗费性能极大;

2、使用定时器到了延时时间再加入队列,如果队列中任务比较多,则延时的精度会大大降低,精度不如Google的方案。

http://www.tj-hxxt.cn/news/18076.html

相关文章:

  • 现在开网站做微商赚钱吗软文推广方案
  • wordpress 增加页面搜索seo优化
  • 一个网站做数据维护3天正常吗无锡百度关键词优化
  • 网站开发技术及特点推广策划方案范文
  • wordpress仿站视频介绍网络营销
  • 做服装行业网站怎么每天更新内容长春网站建设公司哪个好
  • 推荐常州网站建设5年网站seo优化公司
  • 自己做网站是否要买云主机百度帐号登录入口
  • 国外做的比较好的展台网站竞价托管信息
  • 南京奥美广告公司seo关键词排名如何
  • 网站建设制作需要多少钱网络营销该如何发展
  • 宁夏企业网站建设广告营销是做什么的
  • wordpress 主题名称seo关键词优化公司哪家好
  • 做网站要固定ipseo研究中心好客站
  • Wordpress需要费用吗许昌seo公司
  • 网站建设的发票税率推广运营平台
  • 网页设计作业电影介绍网站产品市场营销策划方案
  • 宿迁专业三合一网站开发重庆网站制作系统
  • 毕业设计做网站应该学什么西安网站推广慧创科技
  • 大连电子商务网站建设搜索竞价托管
  • 如皋住房和城乡建设局网站做seo需要哪些知识
  • php网站做分享到朋友圈西安的网络优化公司
  • 做招聘网站赚钱吗上海快速排名优化
  • 网站建设了流程东莞疫情最新情况
  • 广州b2b网站开发价格网站推广工具
  • 详情页设计的原则杭州最专业的seo公司
  • 济宁定制网站建设推广企业邮箱注册
  • 搜索引擎关键词seo优化公司安徽seo人员
  • 做网站软件的网站seo提升
  • 单位网站建设的优势今日头条指数查询