前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android 主线程中 Looper.loop() 为什么不会卡死主线程?

Android 主线程中 Looper.loop() 为什么不会卡死主线程?

作者头像
艳龙
发布2021-12-16 17:40:20
1.4K0
发布2021-12-16 17:40:20
举报
文章被收录于专栏:yanlongli_艳龙

第一次被问到这个问题的时候,就再想,为什么会问这问题呢?

回想了一遍关于Android Handler,Message, MessageQueue 和 Looper 的相关知识,才明白为什么会有这样的问题。

这个问题是怎么来的?

因为Looper.loop() 消息循环是个死循环,会不断的在这里处理MessageQueue消息队列中的消息

代码语言:javascript
复制
 /**
     * Run the message queue in this thread. Be sure to call
     * {@link #quit()} to end the loop.
     */
    public static void loop() {
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;
        /*
         * 删除了一些无关代码
         */
        for (;;) {
            // 这里循环从queue中获取Message消息,如果没有消息的话这里会阻塞
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            try {
                //msg.target 对象是发送Message消息的Handler对象,通过Handler的dispatchMessage进行消息处理
                msg.target.dispatchMessage(msg);
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
             /*
              * 删除了一些无关代码
              */
            // 消息处理完后,将Message放入对象池中,这样Message.obtain()获取Message时候可以减少对象的创建
            msg.recycleUnchecked();
        }
    }

Looper.loop() 为什么不会卡死主线程

1, 这里涉及到Linux pipe/epoll机制 ,简单说就是在主线程的MessageQueue没有消息时,便阻塞在loop的queue.next()中的nativePollOnce()方法里。此时主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生,通过往pipe管道写端写入数据来唤醒主线程工作。这里采用的epoll机制,是一种IO多路复用机制,可以同时监控多个描述符,当某个描述符就绪(读或写就绪),则立刻通知相应程序进行读或写操作,本质同步I/O,即读写是阻塞的。

2,在进入死循环之前创建了新binder线程,在代码ActivityThread.main()中,

thread.attach(false);便会创建一个Binder线程(具体是指ApplicationThread,Binder的服务端,用于接收系统服务AMS发送来的事件),该Binder线程通过Handler将Message发送给主线程.

代码语言:javascript
复制
public static void main(String[] args) {
        ....

        //创建Looper和MessageQueue对象,用于处理主线程的消息
        Looper.prepareMainLooper();

        //创建ActivityThread对象
        ActivityThread thread = new ActivityThread(); 

        //建立Binder通道 (创建新线程)
        thread.attach(false);

        Looper.loop(); //消息循环运行
        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

所以 在 Looper.loop() 中阻塞的时候不会卡死主线程。

Looper.loop() 死循环会特别消耗CPU资源吗?

这里同样是涉及到Linux pipe/epoll机制,可参考不卡死主线程的原因。对Linux pipe/epoll机制有兴趣可google相关的介绍

Looper.loop() 会发生ANR 吗?

下面是ANR在官方文档的介绍:

ANR 如果 Android 应用的界面线程处于阻塞状态的时间过长,会触发“应用无响应”(ANR) 错误。如果应用位于前台,系统会向用户显示一个对话框。ANR 对话框会为用户提供强行退出应用的选项。

可以看到ANR的发生是在程序在处理Message消息的时候,用的时间太长,导致 Looper.loop() 无法进入下一个循环处理后续的消息。

所以 ANR 和 Looper.loop()的阻塞 是两个不同的概念。

  • Looper.loop()阻塞是消息队列为空,在等待新的消息,然后进行处理。
  • ANR 是消息队列不为空的时候,程序在处理某一次的Message时,系统检测耗时太久,提示的ANR。

END!

参考:

https://www.zhihu.com/question/34652589

https://developer.android.com/topic/performance/vitals/anr

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020/4/1 下午,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 这个问题是怎么来的?
  • Looper.loop() 为什么不会卡死主线程
  • Looper.loop() 死循环会特别消耗CPU资源吗?
  • Looper.loop() 会发生ANR 吗?
相关产品与服务
消息队列 CMQ 版
消息队列 CMQ 版(TDMQ for CMQ,简称 TDMQ CMQ 版)是一款分布式高可用的消息队列服务,它能够提供可靠的,基于消息的异步通信机制,能够将分布式部署的不同应用(或同一应用的不同组件)中的信息传递,存储在可靠有效的 CMQ 队列中,防止消息丢失。TDMQ CMQ 版支持多进程同时读写,收发互不干扰,无需各应用或组件始终处于运行状态。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档