主執行緒中的Looper.loop()一直無限迴圈為什麼不會造成ANR?

曉涵說發表於2019-03-09

1.引言 眾所周知在Activity的主執行緒中不能做耗時操作,但是 檢視ActivityThread的原始碼可以看到,該執行緒中包含了一個Loop.looper()的阻塞操作,那麼該阻塞操作為何不會引起ANR? 2.原始碼分析 其實引起ANR的原因主要包括以下兩點:

1.當前的事件沒有機會得到處理(即主執行緒正在處理當前事件,沒有及時完成或looper中的事件分發處理被阻塞); 2.當前事件正在執行,但沒有及時完成。

為了避免ANR的產生,安卓中引入了Handler的處理機制,通過檢視ActivtyThread的原始碼可以看出:

    public static final void main(String[] args) {
        ...
        //建立Looper和MessageQueue
        Looper.prepareMainLooper();
        ...
        //開始輪詢
        Looper.loop();
        ...
    }
複製程式碼

檢視Looper.loop()方法,該方法的操作與我們熟悉的Handelr處理機制類似,分為兩步操作:取出訊息和分發訊息。

   while (true) {
       //取出訊息佇列中的訊息
       Message msg = queue.next(); // might block
       ...
       /根據Message中的target標籤,交給對應的Handle處理
       msg.target.dispatchMessage(msg);
       ...
    }
複製程式碼

因此在ActivityThread的main方法中主要是做訊息的迴圈操作,一旦退出該迴圈操作,那麼當前應用就退出了。 但是該死迴圈是在主執行緒中操作,為何不會引起ANR呢? 通過檢視ActivityThread的handleMessage的原始碼可以看出,Android是由事件驅動的,常見的觸控和Activity的生命週期都是執行在Looper.loop()的控制之下,如果該迴圈停止了,那麼整個應用也停止了。

 public void handleMessage(Message msg) {
        if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
        switch (msg.what) {
            case LAUNCH_ACTIVITY: {
                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
                final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
                r.packageInfo = getPackageInfoNoCheck(r.activityInfo.applicationInfo, r.compatInfo);
                handleLaunchActivity(r, null);
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            }
            break;
            case RELAUNCH_ACTIVITY: {
                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityRestart");
                ActivityClientRecord r = (ActivityClientRecord) msg.obj;
                handleRelaunchActivity(r);
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            }
            break;
            case PAUSE_ACTIVITY:
                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
                handlePauseActivity((IBinder) msg.obj, false, (msg.arg1 & 1) != 0, msg.arg2, (msg.arg1 & 2) != 0);
                maybeSnapshot();
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                break;
            case PAUSE_ACTIVITY_FINISHING:
                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
                handlePauseActivity((IBinder) msg.obj, true, (msg.arg1 & 1) != 0, msg.arg2, (msg.arg1 & 1) != 0);
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                break;
            ...........
        }
    }
複製程式碼

通過檢視上面的原始碼可以看出Activity的整個生命週期都是依靠Looper.loop(),在不同的生命週期執行時傳送不同的訊息,handleMessage接收到不同的Message後,根據case判斷進行相應的處理。

由於Activty的執行是遵循一定的生命週期方法的,因此如果某個週期的方法做過多的耗時操作,必然會影響下個週期的執行時間,整個生命週期的執行就會出現卡頓,繼而會產生ANR的出現。

並且主線的Looper對於訊息的處理時,當子執行緒有訊息傳送時才會被喚醒,但子執行緒沒有訊息傳送時,處於待喚醒狀態,因此不會對CPU的效能產生影響。 3.總結 因此:主執行緒的Looper.loop()中死迴圈本身不會對Activity產生ANR,除非其訊息事件本身的處理存在耗時操作,才會產生ANR.

相關文章