前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >[Android] 为什么主线程不会因为Looper.loop()方法造成阻塞

[Android] 为什么主线程不会因为Looper.loop()方法造成阻塞

作者头像
wOw
发布2020-01-20 16:53:23
2.6K0
发布2020-01-20 16:53:23
举报
文章被收录于专栏:wOw的Android小站wOw的Android小站

首先,关于Handler相关机制,可以参考我之前整理的[Android] Handler消息传递机制

然后我们分解问题,一点点去看这个问题。

Looper.loop和主线程的关系

分析Handler和应用启动的时候讲过,在创建ActivityThread时,主线程会创建Looper,并且进入Loop循环:

代码语言:javascript
复制
public static void main(String[] args) {
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
        SamplingProfilerIntegration.start();

        // CloseGuard defaults to true and can be quite spammy.  We
        // disable it here, but selectively enable it later (via
        // StrictMode) on debug builds, but using DropBox, not logs.
        CloseGuard.setEnabled(false);

        Environment.initForCurrentUser();

        // Set the reporter for event logging in libcore
        EventLogger.setReporter(new EventLoggingReporter());

        // Make sure TrustedCertificateStore looks in the right place for CA certificates
        final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
        TrustedCertificateStore.setDefaultUserDirectory(configDir);

        Process.setArgV0("<pre-initialized>");

  		// 这里sMainLooper赋值
        Looper.prepareMainLooper();

        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }

        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }

        // End of event ActivityThreadMain.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        Looper.loop();	// Looper进入循环

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

loop()函数代码就不贴了,就是一个while(true)的死循环。

所以Looper和主线程的关系是,主线程创建的时候会创建一个MainLooper,并且进入循环。

代码语言:javascript
复制
Looper.prepareMainLooper();
Looper.loop();

主线程阻塞

关于死循环

主线程进入一个死循环,是不是就会被阻塞?

首先,思考一下,如果我们创建一个线程做定时检查某个状态,是不是也会给这个子线程做一个死循环,不断地去循环检查状态。当不需要这个线程的时候,改变flag让这个子线程退出循环并销毁。

想到这就理解,主线程也是一个线程,它也要维持自己的周期,所以也是需要一个死循环的。所以死循环并不是那么让人担心。

关于阻塞

这就涉及到Looper的作用了,Looper里持有一个MessageQueue,这个消息队列存放着外部发来的消息,当有消息过来的时候,Looper就会按顺序把消息一个一个拿出来,进行处理。Looper的loop循环就是一个拿消息的循环。

也就是说,如果你给我发消息,我会立即去拿消息并且做响应。你不给我消息,我就会阻塞,减少CPU消耗(涉及到epoll)。

那么主线程会响应什么消息呢?在ActivityThread里有一个命名为H的handler,它处理所有Activity生命周期有关的事件。因为主线程就是UI线程,当UI发生变化,相关消息就会传进来,Looper就会处理消息。

所以:

Looper的阻塞,前提是没有输入事件,此时MessageQueue是空的,Looper进入空闲,线程进入阻塞,释放CPU,等待输入事件的唤醒。

聊聊ANR

其实担心这个问题的人很多都是被ANR搞怕了,因为ANR就是UI线程做耗时操作了导致卡死状态,然后很多人就在想是不是UI线程进入Loop死循环后,就出现卡死,其实这两个并不是一个问题。

先上结论和上面的做个对比:

UI耗时导致卡死,前提是要有输入事件,此时MessageQueue不是空的,Looper正常轮询,线程并没有阻塞,但是该事件执行时间过长(一般5秒),而且与此期间其他的事件(按键按下,屏幕点击..)都没办法处理(卡死),然后就ANR异常了。

ANR机制的目的还是为了监测主线程的耗时操作,譬如密集CPU运算、大量IO、复杂界面布局等,因为这些都会降低应用程序的响应能力。所以从理念上也能理解,loop死循环只是简单地处理轻量的消息操作,和ANR并没有关系。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019-05-10,如有侵权请联系 cloudcommunity@tencent.com 删除

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

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Looper.loop和主线程的关系
  • 主线程阻塞
    • 关于死循环
      • 关于阻塞
      • 聊聊ANR
      相关产品与服务
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档