android延迟执行优化(安卓开机自启动管理)

首先,需要明确一点,Handler 延时消息机制不是延时发送消息,而是延时去处理消息;举个例子,如下:

handler.postDelayed(() ->{     Log.e("zjt", "delay runnable"); }, 3_000); 

上面的 Handler 不是延时3秒后再发送消息,而是将消息插入消息队列后等3秒后再去处理。

postDelayed 的方法如下:

public final boolean postDelayed(@NonNull Runnable r, long delayMillis) {     return sendMessageDelayed(getPostMessage(r), delayMillis); } 

其中的 getPostMessage 就是将 post 的 runnable 包装成 Message,如下:

private static Message getPostMessage(Runnable r) {     // 使用 Message.obtain() 避免重复创建实例对象,达到节约内存的目的     Message m = Message.obtain();     m.callback = r;     return m; } 

sendMessageDelayed 方法如下:

public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {     if (delayMillis < 0) {         delayMillis = 0;     }     // 延时的时间是手机的开机时间(不包括手机休眠时间)+ 需要延时的时间     return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); } 

sendMessageAtTime 如下:

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); } 

这里面的代码很好理解,就不说了,看看 enqueueMessage:

private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,         long uptimeMillis) {     msg.target = this; // 设置 msg 的 target 为Handler     msg.workSourceUid = ThreadLocalWorkSource.getUid();     // 异步消息,这个需要配合同步屏障来使用,可以看我之前的文章,这里不赘述     if (mAsynchronous) {          msg.setAsynchronous(true);     }     // 插入到 MessageQueue 中     return queue.enqueueMessage(msg, uptimeMillis); } 

MessageQueue 的 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;             // 表示要唤醒 Hander 对应的线程,这个后面解释             needWake = mBlocked;         } 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;         }         // 唤醒Handler对应的线程         if (needWake) {             nativeWake(mPtr);         }     }     return true; } 

举个例子,假设我们消息队列是空的,然后我发送一个延时10s的延时消息,那么会直接把消息存入消息队列。

从消息队列中获取消息是 通过 Looper.loop() 来调用 MessageQueue 的 next()方法,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 iteration      int nextPollTimeoutMillis = 0;      for (;;) {          if (nextPollTimeoutMillis != 0) {              Binder.flushPendingCommands();          }          // 表示要休眠多长时间,功能类似于wait(time)          // -1表示一直休眠,          // 等于0时,不堵塞          // 当有新的消息来时,如果handler对应的线程是阻塞的,那么会唤醒          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) {                      // 计算延时消息的剩余时间                      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;              }               .......              // 判断是否有 idle 任务,即主线程空闲时需要执行的任务,这个下面说               if (pendingIdleHandlerCount <= 0) {                  // 这里表示所有到时间的消息都执行完了,剩下的如果有消息一定是延时且时间还没到的消息;                   // 刚上面的 enqueueMessage 就是根据这个变量来判断是否要唤醒handler对应的线程                  mBlocked = true;                   continue;              }                 ......      }  } 

其实,从这里就可以看出来,Handler 的延时消息是如何实现的了。

比方说 发送一个延时10s的消息,那么在 next()方法是,会阻塞 (10s + 发送消息时的系统开机时间 – 执行next()方法是系统的开机时间),到达阻塞时间时会唤醒。或者这时候有新的消息来了也会 根据 mBlocked = true来唤醒。

IdleHandler是什么?

在 MessageQueue 类中有一个 static 的接口 IdleHanlder:

public static interface IdleHandler {     boolean queueIdle(); } 

MessageQueue中无可处理的Message时回调; 作用:UI线程处理完所有事务后,回调一些额外的操作,且不会堵塞主进程;

接口中只有一个 queueIdle() 函数,线程进入堵塞时执行的额外操作可以写这里, 返回值是true的话,执行完此方法后还会保留这个IdleHandler,否则删除。

秒鲨号所有文章资讯、展示的图片素材等内容均为注册用户上传(部分报媒/平媒内容转载自网络合作媒体),仅供学习参考。用户通过本站上传、发布的任何内容的知识产权归属用户或原始著作权人所有。如有侵犯您的版权,请联系我们反馈!本站将在三个工作日内改正。
(0)

大家都在看

  • windows激活工具哪个好(win10一键永久激活工具)

    今天,给大家带来了一款重量级的win10激活工具–HWIDGen。 以前的kms激活工具,都只是180天循环激活的。 HWIDGen可以实现永久激活,它的激活原理是获取Window…

    2022年2月20日 投稿
  • JJ比赛租号 jj打鱼出租鱼炮 碧蓝航线科技提升

    碧蓝航线科技点数提升的方法:
    1.通过解锁新的特定船舶,可以叠加科技点,获得大量的科技点,激活相应的船舶阵营团队属性增益。
    解锁新船的方法:点击船舶建造→开始建造→选择建造的船舶类型→点击建造抽卡获取。

    2022年6月26日 投稿
  • 银行几点关门(银行5点还是5点半下班)

    作为一名工作了一年多的银行柜员,在入职以前也是懵懂少年,和大多数客户有着同样的疑问,在经历了一年多工作的洗礼之后,想结合自身经历给大家分享一下,以下这些大家可能不了解的银行事。 ‬…

    2022年4月18日 投稿
  • 哪个快递公司比较快(速度最快的快递排名)

    随着网购成为大家日常的习惯,我们与快递业的接触也变得密切起来,而快递是否能安全、快速的到达也成为剁手党关心的事情。所以,许多小伙伴在网购时都习惯问商家发什么快递,因为有些快递的体验…

    2022年6月25日 投稿
  • 怎么看爱奇艺会员几个人在用(爱奇艺怎么设置多人使用会员)

    废话不多说,直接上教程: 下载插件『有范全网通vip视频破解器』(度娘直接搜索进入下载),或者直接进入下面这个网址下载你所对应PC浏览器的插件。 『图文教程』简单几步教你免费观看爱…

    2022年3月8日 投稿
  • win7怎么分区电脑硬盘(合并分区到c盘的步骤)

    风险提示:删除或格式化磁盘的操作将会导致数据丢失,建议您提前备份重要数据以避免任何损失。 题描述: Windows 7系统,在系统下创建分区的时候提示无法在此分配空间中创建新建卷,…

    2022年1月15日 投稿
  • 灾难片排行榜前十名(韩国灾难片排行榜前十)

    你最喜欢的灾难片是哪一部? 在大众的印象中,地震、海啸、空难、海难、瘟疫,所有涉及大型人类群体性伤亡的破坏性事件,都可以算得上灾难片的范畴。 由于受到文化环境的影响,东西方国家对待…

    2022年6月14日 投稿
  • 起亚k5口碑怎么样(12款起亚k5口碑)

    靠近年关,相信不少人都会选择购置一辆新车去犒劳辛苦了一年的自己,但与此同时,大量新手上路加上返乡高峰的到来往往会让交通事故的发生率显著提升。 正因如此,对于安全意识明显增强,打算年…

    2022年6月10日 投稿
  • 手机游戏排行榜前十名(最新手游排行榜前十名)

    一转眼已到2021年12月份,相信这一年玩手游的玩家又增加了不少,作为热爱手游的玩家,总会关注一些游戏榜单,而整个2021年不管是下载榜还是热搜榜,很多手游常年霸榜,比如《王者荣耀…

    2022年6月23日 投稿
  • 多屏互动哪个软件最好(多屏互动电视app)

    多屏互动app是一款针对TCL智能电视的手机控制应用软件。多屏互动app汇聚了多家视频资源,既支持在手机端本地观看,也支持一键投屏到电视大屏观看,同时提供多种辅助工具,从此让你脱离…

    2022年2月27日 投稿
品牌推广 在线咨询
返回顶部