Android中爲何主線程不會由於Looper.loop()裏的死循環卡死?

在知乎上的問題,以爲很好,就轉載過來記錄一下。java

Android程序的運行入口是android.app.ActivityThread類的main()方法。(android-23)android

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

    AndroidKeyStoreProvider.install();

    // 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.loop()源碼可知裏面是一個死循環在遍歷消息隊列取消息。算法

/**
 * Run the message queue in this thread. Be sure to call
 * {@link #quit()} to end the loop.
 */
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;

    // Make sure the identity of this thread is that of the local process,
    // and keep track of what that identity token actually is.
    Binder.clearCallingIdentity();
    final long ident = Binder.clearCallingIdentity();

    for (;;) {
        Message msg = queue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }

        // This must be in a local variable, in case a UI event sets the logger
        Printer logging = me.mLogging;
        if (logging != null) {
            logging.println(">>>>> Dispatching to " + msg.target + " " +
                    msg.callback + ": " + msg.what);
        }

        msg.target.dispatchMessage(msg);

        if (logging != null) {
            logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
        }

        // Make sure that during the course of dispatching the
        // identity of the thread wasn't corrupted.
        final long newIdent = Binder.clearCallingIdentity();
        if (ident != newIdent) {
            Log.wtf(TAG, "Thread identity changed from 0x"
                    + Long.toHexString(ident) + " to 0x"
                    + Long.toHexString(newIdent) + " while dispatching to "
                    + msg.target.getClass().getName() + " "
                    + msg.callback + " what=" + msg.what);
        }

        msg.recycleUnchecked();
    }
}

並且並也沒看見哪裏有相關代碼爲這個死循環準備了一個新線程去運轉,可是主線程卻並不會由於Looper.loop()中的這個死循環卡死,爲何呢?多線程

舉個例子,像Activity的生命週期這些方法這些都是在主線程裏執行的吧,那這些生命週期方法是怎麼實如今死循環體外可以執行起來的?

(1) Android中爲何主線程不會由於Looper.loop()裏的死循環卡死?

這裏涉及線程,先說說說進程/線程,進程:每一個app運行時前首先建立一個進程,該進程是由Zygote fork出來的,用於承載App上運行的各類Activity/Service等組件。進程對於上層應用來講是徹底透明的,這也是google有意爲之,讓App程序都是運行在Android Runtime。大多數狀況一個App就運行在一個進程中,除非在AndroidManifest.xml中配置Android:process屬性,或經過native代碼fork進程。

線程:線程對應用來講很是常見,好比每次new Thread().start都會建立一個新的線程。該線程與App所在進程之間資源共享,從Linux角度來講進程與線程除了是否共享資源外,並無本質的區別,都是一個task_struct結構體,在CPU看來進程或線程無非就是一段可執行的代碼,CPU採用CFS調度算法,保證每一個task都儘量公平的享有CPU時間片

有了這麼準備,再說說死循環問題:

對於線程既然是一段可執行的代碼,當可執行代碼執行完成後,線程生命週期便該終止了,線程退出。而對於主線程,咱們是毫不但願會被運行一段時間,本身就退出,那麼如何保證能一直存活呢?簡單作法就是可執行代碼是能一直執行下去的,死循環便能保證不會被退出,例如,binder線程也是採用死循環的方法,經過循環方式不一樣與Binder驅動進行讀寫操做,固然並不是簡單地死循環,無消息時會休眠。但這裏可能又引起了另外一個問題,既然是死循環又如何去處理其餘事務呢?經過建立新線程的方式。

真正會卡死主線程的操做是在回調方法onCreate/onStart/onResume等操做時間過長,會致使掉幀,甚至發生ANR,looper.loop自己不會致使應用卡死。


(2) 沒看見哪裏有相關代碼爲這個死循環準備了一個新線程去運轉?

事實上,會在進入死循環以前便建立了新binder線程,在代碼ActivityThread.main()中:架構

thread.attach(false);便會建立一個Binder線程(具體是指ApplicationThread,Binder的服務端,用於接收系統服務AMS發送來的事件),該Binder線程經過Handler將Message發送給主線程,具體過程可查看 startService流程分析,這裏不展開說,簡單說Binder用於進程間通訊,採用C/S架構。關於binder感興趣的朋友,可查看我回答的另外一個知乎問題:
爲何Android要採用Binder做爲IPC機制? - Gityuan的回答
 併發

另外,ActivityThread實際上並不是線程,不像HandlerThread類,ActivityThread並無真正繼承Thread類,只是每每運行在主線程,該人以線程的感受,其實承載ActivityThread的主線程就是由Zygote fork而建立的進程。

主線程的死循環一直運行是否是特別消耗CPU資源呢? 其實否則,這裏就涉及到Linux pipe/epoll機制,簡單說就是在主線程的MessageQueue沒有消息時,便阻塞在loop的queue.next()中的nativePollOnce()方法裏,詳情見Android消息機制1-Handler(Java層),此時主線程會釋放CPU資源進入休眠狀態,直到下個消息到達或者有事務發生,經過往pipe管道寫端寫入數據來喚醒主線程工做。這裏採用的epoll機制,是一種IO多路複用機制,能夠同時監控多個描述符,當某個描述符就緒(讀或寫就緒),則馬上通知相應程序進行讀或寫操做,本質同步I/O,即讀寫是阻塞的。 因此說,主線程大多數時候都是處於休眠狀態,並不會消耗大量CPU資源。


(3) Activity的生命週期是怎麼實如今死循環體外可以執行起來的?

ActivityThread的內部類H繼承於Handler,經過handler消息機制,簡單說Handler機制用於同一個進程的線程間通訊。

Activity的生命週期都是依靠主線程的Looper.loop,當收到不一樣Message時則採用相應措施:
在H.handleMessage(msg)方法中,根據接收到不一樣的msg,執行相應的生命週期。

好比收到msg=H.LAUNCH_ACTIVITY,則調用ActivityThread.handleLaunchActivity()方法,最終會經過反射機制,建立Activity實例,而後再執行Activity.onCreate()等方法;
再好比收到msg=H.PAUSE_ACTIVITY,則調用ActivityThread.handlePauseActivity()方法,最終會執行Activity.onPause()等方法。 上述過程,我只挑核心邏輯講,真正該過程遠比這複雜。app

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;
    case STOP_ACTIVITY_SHOW:
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStop");
        handleStopActivity((IBinder)msg.obj, true, msg.arg2);
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        break;
    case STOP_ACTIVITY_HIDE:
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStop");
        handleStopActivity((IBinder)msg.obj, false, msg.arg2);
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        break;
    case SHOW_WINDOW:
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityShowWindow");
        handleWindowVisibility((IBinder)msg.obj, true);
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        break;
    case HIDE_WINDOW:
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityHideWindow");
        handleWindowVisibility((IBinder)msg.obj, false);
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        break;
    case RESUME_ACTIVITY:
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityResume");
        handleResumeActivity((IBinder) msg.obj, true, msg.arg1 != 0, true);
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        break;
    case SEND_RESULT:
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityDeliverResult");
        handleSendResult((ResultData)msg.obj);
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        break;
    case DESTROY_ACTIVITY:
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityDestroy");
        handleDestroyActivity((IBinder)msg.obj, msg.arg1 != 0,
                msg.arg2, false);
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        break;
    case BIND_APPLICATION:
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication");
        AppBindData data = (AppBindData)msg.obj;
        handleBindApplication(data);
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        break;
    case EXIT_APPLICATION:
        if (mInitialApplication != null) {
            mInitialApplication.onTerminate();
        }
        Looper.myLooper().quit();
        break;
    case NEW_INTENT:
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityNewIntent");
        handleNewIntent((NewIntentData)msg.obj);
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        break;
    case RECEIVER:
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "broadcastReceiveComp");
        handleReceiver((ReceiverData)msg.obj);
        maybeSnapshot();
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        break;
    case CREATE_SERVICE:
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceCreate");
        handleCreateService((CreateServiceData)msg.obj);
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        break;
    case BIND_SERVICE:
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceBind");
        handleBindService((BindServiceData)msg.obj);
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        break;
    case UNBIND_SERVICE:
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceUnbind");
        handleUnbindService((BindServiceData)msg.obj);
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        break;
    case SERVICE_ARGS:
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceStart");
        handleServiceArgs((ServiceArgsData)msg.obj);
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        break;
    case STOP_SERVICE:
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceStop");
        handleStopService((IBinder)msg.obj);
        maybeSnapshot();
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        break;
    case CONFIGURATION_CHANGED:
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "configChanged");
        mCurDefaultDisplayDpi = ((Configuration)msg.obj).densityDpi;
        handleConfigurationChanged((Configuration)msg.obj, null);
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        break;
    ............................
    }
    if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString(msg.what));
}



主線程的消息又是哪來的呢?固然是App進程中的其餘線程經過Handler發送給主線程,請看接下來的內容:


---------------------------------------------------------------------------------------------------------------------------
最後,從進程與線程間通訊的角度,經過一張圖加深你們對App運行過程的理解:框架



system_server進程是系統進程,java framework框架的核心載體,裏面運行了大量的系統服務,好比這裏提供ApplicationThreadProxy(簡稱ATP),ActivityManagerService(簡稱AMS),這個兩個服務都運行在system_server進程的不一樣線程中,因爲ATP和AMS都是基於IBinder接口,都是binder線程,binder線程的建立與銷燬都是由binder驅動來決定的。

App進程則是咱們常說的應用程序,主線程主要負責Activity/Service等組件的生命週期以及UI相關操做都運行在這個線程; 另外,每一個App進程中至少會有兩個binder線程 ApplicationThread(簡稱AT)和ActivityManagerProxy(簡稱AMP),除了圖中畫的線程,其中還有不少線程,好比signal catcher線程等,這裏就不一一列舉。

Binder用於不一樣進程之間通訊,由一個進程的Binder客戶端向另外一個進程的服務端發送事務,好比圖中線程2向線程4發送事務;而handler用於同一個進程中不一樣線程的通訊,好比圖中線程4向主線程發送消息。

結合圖說說Activity生命週期,好比暫停Activity,流程以下:ide

  1. 線程1的AMS中調用線程2的ATP;(因爲同一個進程的線程間資源共享,能夠相互直接調用,但須要注意多線程併發問題)
  2. 線程2經過binder傳輸到App進程的線程4;
  3. 線程4經過handler消息機制,將暫停Activity的消息發送給主線程;
  4. 主線程在looper.loop()中循環遍歷消息,當收到暫停Activity的消息時,便將消息分發給ActivityThread.H.handleMessage()方法,再通過方法的調用,最後便會調用到Activity.onPause(),當onPause()處理完後,繼續循環loop下去。
相關文章
相關標籤/搜索