美文网首页Android开发经验谈Android开发
Handler中Looper死循环为什么不会导致应用卡死?

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

作者: 程序媛饭冰冰 | 来源:发表于2020-06-09 20:24 被阅读0次

    应用卡死,也就是ANR所产生的原因?

    1、5秒钟之内没有响应输入的事件,比如按键、屏幕触摸等。

    2、广播接收器在10秒内没有执行完毕。

    为什么说应用所有的操作都是在loop()中来管理?

    首先,我们的每一个应用都存在于自己的虚拟机中,也就是说每一个应用都有自己的一个main函数,这个main函数就是ActivityThread.java的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");
        }
    

    为什么每一个应用会有自己的一个main函数呢?

    当我们在launcher界面启动一个应用的时候,这时候,系统就会用zygote给我们分配一个虚拟机,然后,这个应用就会运行在这个虚拟机上面。

    应用运行到虚拟机之后,首先它要执行的就是启动ActivityThread,在ActivityThread中,它又会启动它的main()函数。

    在main()函数中,它最重要的两行代码:

        public static void main(String[] args) {
     
            ...
     
            Looper.prepareMainLooper();
     
            ...
     
            Looper.loop();
        }
    

    所以在程序运行的时候,主线程所有的代码都运行在这个Looper里面。

    也就是说应用所有生命周期的函数(包括Activity、Service所有生命周期)都运行在这个Looper里面,而且,它们都是以消息的方式存在的。

    假如说一个Activity启动,要走onResume()函数的时候,它就会在Activity的H里面执行RESUME_ACTIVITY。

                        case RESUME_ACTIVITY: return "RESUME_ACTIVITY";
    

    它发送了一个Resume的消息,再接着看下这个Resume这个消息做了什么事情,代码在ActivityThread.java的handleMessage中。

                    case RESUME_ACTIVITY:
                        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityResume");
                        SomeArgs args = (SomeArgs) msg.obj;
                        handleResumeActivity((IBinder) args.arg1, true, args.argi1 != 0, true,
                                args.argi3, "RESUME_ACTIVITY");
                        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                        break;
    

    进入handleResumeActivity()方法。

            // TODO Push resumeArgs into the activity for consideration
            r = performResumeActivity(token, clearHide, reason);
    

    它触发了Activity的管理机制,在Activity的管理机制里面,他就会发送一个消息,这个消息,就是对Activity进行Resume的操作。

    r.activity.performResume();
    

    接着往performResume()方法里面走,就进入了Activity.java。

        final void performResume() {
     
            ....
     
            mFragments.dispatchResume();
            mFragments.execPendingActions();
     
            ....
        }
    

    再后面就调用了Fragemet的管理机制。

        public void dispatchResume() {
            mHost.mFragmentManager.dispatchResume();
        }
    

    因为类的继承,到最终是继承自了FragmentActivity,所以最后又变成了对Fragment的Resume。

    既然Handler的消息全都是loop来的,为什么我们没有ANR问题?

    其实产生ANR问题的不是Looper.loop(),哪怕主线程正在等待(block)。

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

    因为这个时候(阻塞)的时候,说明主线程在休眠。

    之前不是说5秒钟不相应就会出现阻塞问题吗,为什么休眠个好长时间也并不会被ANR呢?

    了解这个问题,先看唤醒主线程的方式有哪些:

    1、输入事件

    主线程虽然被block了,但与ANR的问题是没有关系的,只要输入事件有响应,他会唤醒,就不会被block了。

    所以,产生ANR的问题不是因为主线程睡眠了,而是因为输入事件没有响应,输入事件没有响应他就没有办法唤醒这个Looper,才加了这个5秒的限制。

    2、往Looper里面添加消息的时候,它会唤醒这个Looper。

    因为应用中不管是Activity,还是Service,所有的操作都是在各自的生命周期中执行的,所以它所有的操作都逃不出生命周期。所以,所有的操作都执行在ActivityThread.java中的loop()里面,所以,应用所有的操作都是在这个loop()中来管理的,也正是因为这个原因,主线程的loop()是不能够退出去的。

    只有一种情况,我们在一个应用的一个界面下不动,这个应用没有任何事件发生,也没有任何别的事件要处理,这个时候,我们的Looper就处于一个block状态;当点击一下这个屏幕,他就会触发唤醒这个Looper。

    总结

    为什么没有导致应用卡死?

    因为应用卡死压根与这个Looper没有关系,应用在没有消息需要处理的时候,它是在睡眠,释放线程;卡死是ANR,而Looper是睡眠。

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

    因为主线程在接受到其他消息的时候没有时间去响应,它的时间都在处理那一个耗时的操作,造成点击事件没有办法响应,点击事件没有办法响应就容易出现ANR。

    原文作者:Seas.Su
    原文链接:https://blog.csdn.net/yichen97/java/article/details/106367067

    相关文章

      网友评论

        本文标题:Handler中Looper死循环为什么不会导致应用卡死?

        本文链接:https://www.haomeiwen.com/subject/mpietktx.html