android 的 IdleHandler 你了解嘛??

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

在 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 收购注资,并拉拢多家制造商组成开放手机联盟开发改良,逐渐扩展到到平板电脑及其他领域上。

    333 引用 • 323 回帖 • 70 关注

相关帖子

欢迎来到这里!

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

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

推荐标签 标签

  • Caddy

    Caddy 是一款默认自动启用 HTTPS 的 HTTP/2 Web 服务器。

    10 引用 • 54 回帖 • 130 关注
  • 音乐

    你听到信仰的声音了么?

    59 引用 • 509 回帖
  • PHP

    PHP(Hypertext Preprocessor)是一种开源脚本语言。语法吸收了 C 语言、 Java 和 Perl 的特点,主要适用于 Web 开发领域,据说是世界上最好的编程语言。

    164 引用 • 407 回帖 • 526 关注
  • RIP

    愿逝者安息!

    8 引用 • 92 回帖 • 293 关注
  • MySQL

    MySQL 是一个关系型数据库管理系统,由瑞典 MySQL AB 公司开发,目前属于 Oracle 公司。MySQL 是最流行的关系型数据库管理系统之一。

    675 引用 • 535 回帖
  • etcd

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

    5 引用 • 26 回帖 • 492 关注
  • Pipe

    Pipe 是一款小而美的开源博客平台。Pipe 有着非常活跃的社区,可将文章作为帖子推送到社区,来自社区的回帖将作为博客评论进行联动(具体细节请浏览 B3log 构思 - 分布式社区网络)。

    这是一种全新的网络社区体验,让热爱记录和分享的你不再感到孤单!

    131 引用 • 1114 回帖 • 150 关注
  • Oracle

    Oracle(甲骨文)公司,全称甲骨文股份有限公司(甲骨文软件系统有限公司),是全球最大的企业级软件公司,总部位于美国加利福尼亚州的红木滩。1989 年正式进入中国市场。2013 年,甲骨文已超越 IBM,成为继 Microsoft 后全球第二大软件公司。

    103 引用 • 126 回帖 • 453 关注
  • Tomcat

    Tomcat 最早是由 Sun Microsystems 开发的一个 Servlet 容器,在 1999 年被捐献给 ASF(Apache Software Foundation),隶属于 Jakarta 项目,现在已经独立为一个顶级项目。Tomcat 主要实现了 JavaEE 中的 Servlet、JSP 规范,同时也提供 HTTP 服务,是市场上非常流行的 Java Web 容器。

    162 引用 • 529 回帖
  • Q&A

    提问之前请先看《提问的智慧》,好的问题比好的答案更有价值。

    6522 引用 • 29305 回帖 • 248 关注
  • Webswing

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

    1 引用 • 15 回帖 • 635 关注
  • Hprose

    Hprose 是一款先进的轻量级、跨语言、跨平台、无侵入式、高性能动态远程对象调用引擎库。它不仅简单易用,而且功能强大。你无需专门学习,只需看上几眼,就能用它轻松构建分布式应用系统。

    9 引用 • 17 回帖 • 597 关注
  • 一些有用的避坑指南。

    69 引用 • 93 回帖
  • C++

    C++ 是在 C 语言的基础上开发的一种通用编程语言,应用广泛。C++ 支持多种编程范式,面向对象编程、泛型编程和过程化编程。

    106 引用 • 152 回帖 • 1 关注
  • 黑曜石

    黑曜石是一款强大的知识库工具,支持本地 Markdown 文件编辑,支持双向链接和关系图。

    A second brain, for you, forever.

    10 引用 • 85 回帖
  • 阿里云

    阿里云是阿里巴巴集团旗下公司,是全球领先的云计算及人工智能科技公司。提供云服务器、云数据库、云安全等云计算服务,以及大数据、人工智能服务、精准定制基于场景的行业解决方案。

    89 引用 • 345 回帖 • 1 关注
  • Ant-Design

    Ant Design 是服务于企业级产品的设计体系,基于确定和自然的设计价值观上的模块化解决方案,让设计者和开发者专注于更好的用户体验。

    17 引用 • 23 回帖
  • 创造

    你创造的作品可能会帮助到很多人,如果是开源项目的话就更赞了!

    172 引用 • 990 回帖
  • Android

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

    333 引用 • 323 回帖 • 70 关注
  • 服务

    提供一个服务绝不仅仅是简单的把硬件和软件累加在一起,它包括了服务的可靠性、服务的标准化、以及对服务的监控、维护、技术支持等。

    41 引用 • 24 回帖 • 4 关注
  • InfluxDB

    InfluxDB 是一个开源的没有外部依赖的时间序列数据库。适用于记录度量,事件及实时分析。

    2 引用 • 54 关注
  • ZooKeeper

    ZooKeeper 是一个分布式的,开放源码的分布式应用程序协调服务,是 Google 的 Chubby 一个开源的实现,是 Hadoop 和 HBase 的重要组件。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。

    59 引用 • 29 回帖 • 15 关注
  • Thymeleaf

    Thymeleaf 是一款用于渲染 XML/XHTML/HTML5 内容的模板引擎。类似 Velocity、 FreeMarker 等,它也可以轻易的与 Spring 等 Web 框架进行集成作为 Web 应用的模板引擎。与其它模板引擎相比,Thymeleaf 最大的特点是能够直接在浏览器中打开并正确显示模板页面,而不需要启动整个 Web 应用。

    11 引用 • 19 回帖 • 317 关注
  • 链书

    链书(Chainbook)是 B3log 开源社区提供的区块链纸质书交易平台,通过 B3T 实现共享激励与价值链。可将你的闲置书籍上架到链书,我们共同构建这个全新的交易平台,让闲置书籍继续发挥它的价值。

    链书社

    链书目前已经下线,也许以后还有计划重制上线。

    14 引用 • 257 回帖
  • API

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

    76 引用 • 421 回帖
  • 星云链

    星云链是一个开源公链,业内简单的将其称为区块链上的谷歌。其实它不仅仅是区块链搜索引擎,一个公链的所有功能,它基本都有,比如你可以用它来开发部署你的去中心化的 APP,你可以在上面编写智能合约,发送交易等等。3 分钟快速接入星云链 (NAS) 测试网

    3 引用 • 16 回帖 • 5 关注
  • LaTeX

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

    9 引用 • 32 回帖 • 168 关注