android 的 IdleHandler 你了解嘛??

本贴最后更新于 2128 天前,其中的信息可能已经事过境迁

在 Android 中,Handler 是我们最常用的消息处理类,也是系统中非常重要的类,所以它的原理我们也应该十分的清楚了解,所以 Handler,Looper,MessageQueue 成了我们必须学习和理解的东西,平常我们都通过 Handler 向 Looper 包含的 MessageQueue 投递 Message,比如这样:

new Handler(Looper.getMainLooper()).post(new Runnable() { @Override public void run() {/do something } });

但是其中有一个接口很少被提及和使用,那就是 IdleHandler,其实它内部的 IdleHandler 接口有很多有趣的用法,首先看看它的定义:

/** * Callback interface for discovering when a thread is going to block * waiting for more messages. */ public static interface IdleHandler { /** * Called when the message queue has run out of messages and will now * wait for more. Return true to keep your idle handler active, false * to have it removed. This may be called if there are still messages * pending in the queue, but they are all scheduled to be dispatched * after the current time. */ boolean queueIdle(); }

简而言之,就是在 looper 里面的 message 暂时处理完了,这个时候会回调这个接口,返回 false,那么就会移除它,返回 true 就会在下次 message 处理完了的时候继续回调

下面我们来看下 IdleHandler 在 Handler 空闲时执行案例代码:

public class MainActivity extends Activity { private Handler mHandler; private int mWhat = 0; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mHandler = new Handler(){ @Override public void handleMessage(Message msg) { Log.d("main", "我在执行,你想回来,我用平底锅打飞你!"); try { Thread.sleep(1500); } catch (InterruptedException e) { e.printStackTrace(); } super.handleMessage(msg); } }; Button btn = (Button) findViewById(R.id.but); btn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { for(int i=0;i<10;i++){ mHandler.sendEmptyMessage(mWhat); } } }); } @Override protected void onResume() { super.onResume(); Looper.myQueue().addIdleHandler(new MyIdleOnce()); Looper.myQueue().addIdleHandler(new MyIdleKeep()); } class MyIdleKeep implements MessageQueue.IdleHandler{ /** *返回值为true,则保持此Idle一直在Handler中,否则,执行一次后就从Handler线程中remove掉。 */ @Override public boolean queueIdle() { Log.d("main","我是空闲线程,我还会回来的!"); return true; } } class MyIdleOnce implements MessageQueue.IdleHandler{ @Override public boolean queueIdle() { Log.d("main","我是初恋,我只在你的生命中出现一次,我发誓,你会想我的!"); return false; } } }

此 Activity 有两个 IdleHandler 消息,我们执行此 Activity 时,MyIdleKeep 消息和 MyIdleOnce 会依次执行。如果 IdleHandler 的 queueIdle 方法返回 false,那么 IdleHandler 执行完,就会从 IdleHandler 移除。

Log 输出如下:

2019-10-24 11:29:22.439 8936-8936/com.example.myapplication D/main: 我是空闲线程,我还会回来的! 2019-10-24 11:29:22.617 8936-8936/com.example.myapplication D/main: 我是空闲线程,我还会回来的! 2019-10-24 11:29:22.617 8936-8936/com.example.myapplication D/main: 我是初恋,我只在你的生命中出现一次,我发誓,你会想我的! 2019-10-24 11:29:22.617 8936-8936/com.example.myapplication D/main: 我是空闲线程,我还会回来的! 2019-10-24 11:29:22.680 8936-8936/com.example.myapplication D/main: 我是空闲线程,我还会回来的! 2019-10-24 11:29:22.798 8936-8936/com.example.myapplication D/main: 我是空闲线程,我还会回来的! 2019-10-24 11:29:22.798 8936-8936/com.example.myapplication D/main: 我是空闲线程,我还会回来的! 2019-10-24 11:29:51.819 8936-8936/com.example.myapplication D/main: 我是空闲线程,我还会回来的! 2019-10-24 11:29:51.819 8936-8936/com.example.myapplication D/main: 我是空闲线程,我还会回来的! 2019-10-24 11:29:51.857 8936-8936/com.example.myapplication D/main: 我在执行,你想回来,我用平底锅打飞你! 2019-10-24 11:29:59.364 8936-8936/com.example.myapplication D/main: 我在执行,你想回来,我用平底锅打飞你! 2019-10-24 11:30:00.866 8936-8936/com.example.myapplication D/main: 我在执行,你想回来,我用平底锅打飞你! 2019-10-24 11:30:02.367 8936-8936/com.example.myapplication D/main: 我在执行,你想回来,我用平底锅打飞你! 2019-10-24 11:30:03.868 8936-8936/com.example.myapplication D/main: 我在执行,你想回来,我用平底锅打飞你! 2019-10-24 11:30:05.369 8936-8936/com.example.myapplication D/main: 我在执行,你想回来,我用平底锅打飞你! 2019-10-24 11:30:06.892 8936-8936/com.example.myapplication D/main: 我是空闲线程,我还会回来的! 2019-10-24 11:30:07.004 8936-8936/com.example.myapplication D/main: 我是空闲线程,我还会回来的!

IdleHandler 在 MessageQueue 与 Looper 和 Handler 是什么关系?

  • 原理源码分析如下:
/** * 获取当前线程队列使用Looper.myQueue(),获取主线程队列可用getMainLooper().myQueue() */ public final class MessageQueue { ...... /** * 当前队列将进入阻塞等待消息时调用该接口回调,即队列空闲 */ public static interface IdleHandler { /** * 返回true就是单次回调后不删除,下次进入空闲时继续回调该方法,false只回调单次。 */ boolean queueIdle(); } /** * <p>This method is safe to call from any thread. * 判断当前队列是不是空闲的,辅助方法 */ public boolean isIdle() { synchronized (this) { final long now = SystemClock.uptimeMillis(); return mMessages == null || now < mMessages.when; } } /** * <p>This method is safe to call from any thread. * 添加一个IdleHandler到队列,如果IdleHandler接口方法返回false则执行完会自动删除, * 否则需要手动removeIdleHandler。 */ public void addIdleHandler(@NonNull IdleHandler handler) { if (handler == null) { throw new NullPointerException("Can't add a null IdleHandler"); } synchronized (this) { mIdleHandlers.add(handler); } } /** * <p>This method is safe to call from any thread. * 删除一个之前添加的 IdleHandler。 */ public void removeIdleHandler(@NonNull IdleHandler handler) { synchronized (this) { mIdleHandlers.remove(handler); } } ...... //Looper的prepare()方法会通过ThreadLocal准备当前线程的MessageQueue实例, //然后在loop()方法中死循环调用当前队列的next()方法获取Message。 Message next() { ...... for (;;) { ...... nativePollOnce(ptr, nextPollTimeoutMillis); synchronized (this) { ...... //把通过addIdleHandler添加的IdleHandler转成数组存起来在mPendingIdleHandlers中 // 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. //循环遍历所有IdleHandler 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 { //调用IdleHandler接口的queueIdle方法并获取返回值。 keep = idler.queueIdle(); } catch (Throwable t) { Log.wtf(TAG, "IdleHandler threw exception", t); } //如果IdleHandler接口的queueIdle方法返回false说明只执行一次需要删除。 if (!keep) { synchronized (this) { mIdleHandlers.remove(idler); } } } ...... } } }
  • Android

    Android 是一种以 Linux 为基础的开放源码操作系统,主要使用于便携设备。2005 年由 Google 收购注资,并拉拢多家制造商组成开放手机联盟开发改良,逐渐扩展到到平板电脑及其他领域上。

    337 引用 • 324 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...

推荐标签 标签

  • QQ

    1999 年 2 月腾讯正式推出“腾讯 QQ”,在线用户由 1999 年的 2 人(马化腾和张志东)到现在已经发展到上亿用户了,在线人数超过一亿,是目前使用最广泛的聊天软件之一。

    45 引用 • 557 回帖 • 2 关注
  • Mobi.css

    Mobi.css is a lightweight, flexible CSS framework that focus on mobile.

    1 引用 • 6 回帖 • 777 关注
  • etcd

    etcd 是一个分布式、高可用的 key-value 数据存储,专门用于在分布式系统中保存关键数据。

    6 引用 • 26 回帖 • 545 关注
  • 友情链接

    确认过眼神后的灵魂连接,站在链在!

    24 引用 • 373 回帖 • 2 关注
  • Webswing

    Webswing 是一个能将任何 Swing 应用通过纯 HTML5 运行在浏览器中的 Web 服务器,详细介绍请看 将 Java Swing 应用变成 Web 应用

    1 引用 • 15 回帖 • 651 关注
  • ZeroNet

    ZeroNet 是一个基于比特币加密技术和 BT 网络技术的去中心化的、开放开源的网络和交流系统。

    1 引用 • 21 回帖 • 653 关注
  • OnlyOffice
    4 引用 • 17 关注
  • LaTeX

    LaTeX(音译“拉泰赫”)是一种基于 ΤΕΧ 的排版系统,由美国计算机学家莱斯利·兰伯特(Leslie Lamport)在 20 世纪 80 年代初期开发,利用这种格式,即使使用者没有排版和程序设计的知识也可以充分发挥由 TeX 所提供的强大功能,能在几天,甚至几小时内生成很多具有书籍质量的印刷品。对于生成复杂表格和数学公式,这一点表现得尤为突出。因此它非常适用于生成高印刷质量的科技和数学类文档。

    12 引用 • 59 回帖 • 3 关注
  • 书籍

    宋真宗赵恒曾经说过:“书中自有黄金屋,书中自有颜如玉。”

    84 引用 • 414 回帖 • 1 关注
  • Log4j

    Log4j 是 Apache 开源的一款使用广泛的 Java 日志组件。

    20 引用 • 18 回帖 • 46 关注
  • Scala

    Scala 是一门多范式的编程语言,集成面向对象编程和函数式编程的各种特性。

    13 引用 • 11 回帖 • 166 关注
  • API

    应用程序编程接口(Application Programming Interface)是一些预先定义的函数,目的是提供应用程序与开发人员基于某软件或硬件得以访问一组例程的能力,而又无需访问源码,或理解内部工作机制的细节。

    79 引用 • 431 回帖 • 1 关注
  • OpenShift

    红帽提供的 PaaS 云,支持多种编程语言,为开发人员提供了更为灵活的框架、存储选择。

    14 引用 • 20 回帖 • 668 关注
  • Git

    Git 是 Linux Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控制软件。

    211 引用 • 358 回帖 • 1 关注
  • SendCloud

    SendCloud 由搜狐武汉研发中心孵化的项目,是致力于为开发者提供高质量的触发邮件服务的云端邮件发送平台,为开发者提供便利的 API 接口来调用服务,让邮件准确迅速到达用户收件箱并获得强大的追踪数据。

    2 引用 • 8 回帖 • 513 关注
  • 锤子科技

    锤子科技(Smartisan)成立于 2012 年 5 月,是一家制造移动互联网终端设备的公司,公司的使命是用完美主义的工匠精神,打造用户体验一流的数码消费类产品(智能手机为主),改善人们的生活质量。

    4 引用 • 31 回帖 • 1 关注
  • 以太坊

    以太坊(Ethereum)并不是一个机构,而是一款能够在区块链上实现智能合约、开源的底层系统。以太坊是一个平台和一种编程语言 Solidity,使开发人员能够建立和发布下一代去中心化应用。 以太坊可以用来编程、分散、担保和交易任何事物:投票、域名、金融交易所、众筹、公司管理、合同和知识产权等等。

    34 引用 • 367 回帖
  • IDEA

    IDEA 全称 IntelliJ IDEA,是一款 Java 语言开发的集成环境,在业界被公认为最好的 Java 开发工具之一。IDEA 是 JetBrains 公司的产品,这家公司总部位于捷克共和国的首都布拉格,开发人员以严谨著称的东欧程序员为主。

    181 引用 • 400 回帖
  • Firefox

    Mozilla Firefox 中文俗称“火狐”(正式缩写为 Fx 或 fx,非正式缩写为 FF),是一个开源的网页浏览器,使用 Gecko 排版引擎,支持多种操作系统,如 Windows、OSX 及 Linux 等。

    7 引用 • 30 回帖 • 368 关注
  • Docker

    Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的操作系统上。容器完全使用沙箱机制,几乎没有性能开销,可以很容易地在机器和数据中心中运行。

    498 引用 • 934 回帖 • 1 关注
  • OneDrive
    2 引用
  • Dubbo

    Dubbo 是一个分布式服务框架,致力于提供高性能和透明化的 RPC 远程服务调用方案,是 [阿里巴巴] SOA 服务化治理方案的核心框架,每天为 2,000+ 个服务提供 3,000,000,000+ 次访问量支持,并被广泛应用于阿里巴巴集团的各成员站点。

    60 引用 • 82 回帖 • 622 关注
  • Spark

    Spark 是 UC Berkeley AMP lab 所开源的类 Hadoop MapReduce 的通用并行框架。Spark 拥有 Hadoop MapReduce 所具有的优点;但不同于 MapReduce 的是 Job 中间输出结果可以保存在内存中,从而不再需要读写 HDFS,因此 Spark 能更好地适用于数据挖掘与机器学习等需要迭代的 MapReduce 的算法。

    74 引用 • 46 回帖 • 565 关注
  • jQuery

    jQuery 是一套跨浏览器的 JavaScript 库,强化 HTML 与 JavaScript 之间的操作。由 John Resig 在 2006 年 1 月的 BarCamp NYC 上释出第一个版本。全球约有 28% 的网站使用 jQuery,是非常受欢迎的 JavaScript 库。

    63 引用 • 134 回帖 • 742 关注
  • GAE

    Google App Engine(GAE)是 Google 管理的数据中心中用于 WEB 应用程序的开发和托管的平台。2008 年 4 月 发布第一个测试版本。目前支持 Python、Java 和 Go 开发部署。全球已有数十万的开发者在其上开发了众多的应用。

    14 引用 • 42 回帖 • 837 关注
  • FreeMarker

    FreeMarker 是一款好用且功能强大的 Java 模版引擎。

    23 引用 • 20 回帖 • 471 关注
  • TextBundle

    TextBundle 文件格式旨在应用程序之间交换 Markdown 或 Fountain 之类的纯文本文件时,提供更无缝的用户体验。

    1 引用 • 2 回帖 • 83 关注