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

应用卡死,也就是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。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章