Handler中Looper死循环为什么不会导致应用卡死?

先来列举一下一般出现ANR的情况:
1.键盘输入事件、触摸无响应5s;
2.Receiver广播接收器10s超时无响应;
3.Service服务20s超时无响应;
...

再简单介绍一下

Handler会在应用启动之后,在ActivityThread的main 方法中启动:

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

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

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

这里我们可以看到三个关键行:
Looper.prepareMainLooper();

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

...
Looper.loop(); //循环

Looperloop方法会从MessageQueue中next方法中取出message

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 (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
			...

            msg.recycleUnchecked();
        }
}

Message msg = queue.next(); // might block

而next方法会调用native方法 nativePollOnce

Message next() {
      
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }

        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }

            nativePollOnce(ptr, nextPollTimeoutMillis);

            ...
        }
}

MessageQueue中没有消息时,queue.next()会阻塞在nativePollOnce()方法中,当nativePollOnce()被阻塞时,主线程会释放CPU资源,进入休眠状态。
直到获取到新的Message,就会唤醒主线程继续工作。

这里主线程进入休眠状态和死循环是有区别的:

休眠状态是指在内核状态里,主线程被挂起,线程状态转移到休眠状态。

死循环是指卡死是在主线程中执行一个耗时的操作,loop()会一直在处理一个消息,而for循环中有很多消息需要被处理,而这一个消息要处理很久,这一个消息的处理时间,会转变成其他的点击事件没有响应,从而触发ANR。

 

 

上一篇:【面试专题】2021年大厂 Handler 面试题集合


下一篇:Android Handler整体梳理以及热点问题解析