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


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

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

Looper.loop和主线程的关系

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

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("");

          // 这里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,并且进入循环。

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并没有关系。


文章作者: Wossoneri
版权声明: 本博客所有文章除特別声明外,均采用 CC BY-NC 4.0 许可协议。转载请注明来源 Wossoneri !
评论
  目录