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。

 

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