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

北京市建设监理协会网站谷歌商店下载官方正版

北京市建设监理协会网站,谷歌商店下载官方正版,网站被k申诉,青海保险网站建设公司Handler postDelayed的实现原理 问题描述 Handler.postDelayed()的原理是如何保证延时执行的#xff1f; 扩展#xff1a;这样实现的好处是什么#xff1f; 题目分析 猜测一下 以我们对Handler的了解#xff0c;内部使用了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 dont 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/225591.html

相关文章:

  • 安徽省建设厅网站工程师查询软件开发外包是什么意思
  • 浙江省建设注册管理中心网站首页淄博网站排名优化公司
  • 大理石在哪些网站做宣传阿里云网站域名查询
  • 下载ppt模板幻灯片模板关键词排名优化外包
  • 萍乡网站优化seo联盟平台
  • 高端定制网站建设高端旅游定制wordpress创业模式
  • 网站开发图片压缩百度官方免费下载
  • 贵阳58同城做网站公司沈阳市和平区建设局网站
  • 做网站端口内容无法替换大城网站建设
  • 网站建设需要啥静态网站建设的技术运用
  • 知名网站排行榜基金会网站建设方案
  • 做网站分辨率多少班级网站建设组织机构
  • 深圳营销网站企业网站建设公司制作平台
  • 企业网站建设 西宁网站引导页html模板
  • 茶叶网站建设哪家做网站常用字体
  • 网站添加cnzzwordpress主题如何使用教程
  • 做网站和微信公众号需要多少钱wordpress教程全集(入门到精通)
  • wordpress 跨站什么是网络营销有哪些特点
  • 建设银行网站查询企业推广的主要目的是
  • 南昌天和建设有限公司网站传媒公司官网
  • 做网站最简单的工具信息流优化师职业规划
  • 重庆有名的网站建设网站建设问题新闻资讯
  • 网站开发服务器多少钱中国互联网百强企业排名
  • 网站建设及维护价钱昆明seo代理商
  • 手机网站建设图片素材设置网络的网站
  • 长沙平面设计公司都有哪些四川seo推广公司
  • 网站开发通用流程图公司做网站需要哪些手续
  • 廊坊网站推广排名品牌网站建设优化公司排名
  • 在线购物网站建设流程图深圳网站建设公司哪家
  • 顺德o2o网站建设免费高清视频素材app哪里找