1. 程式人生 > >Android的Looper的無限迴圈為啥不會ANR?

Android的Looper的無限迴圈為啥不會ANR?

借鑑自知乎https://www.zhihu.com/question/34652589,gityuan

ActivityThread中的程式碼

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();
    ...
    Looper.loop();

Looper.prepareMainLooper();這裡就開啟了主執行緒的Looper的無限迴圈。但是為什麼不會ANR呢?

主執行緒是UI執行緒,阻塞的話,就會掉幀。

下面轉述一下gityuan大神的話

執行緒執行一段時間後,工作做完了,執行緒的生命週期就終止了。為了保活,主執行緒和Binder執行緒都是通過無限迴圈的方式。當然不是簡單的無限迴圈,無訊息的時候會休眠。但是又有一個問題,既然死迴圈,後面Activity的建立相關事務又怎麼去執行?通過建立新執行緒的方式。

建立新執行緒的程式碼在哪

Looper.prepareMainLooper();

ActivityThread thread = new ActivityThread();
thread.attach(false);
在Looper初始化程式碼的下方一點點thread.attach(false);這句話。會建立一個Binder執行緒(具體是指ApplicationThread,Binder的服務端,用於接收系統服務AMS傳送來的事件),該Binder執行緒通過Handler將Message傳送給主執行緒

主執行緒的死迴圈特別消耗CPU資源嗎

這裡就涉及到Linux pipe/epoll機制,簡單說就是在主執行緒的MessageQueue沒有訊息時,便阻塞在loop的queue.next()中的nativePollOnce()方法裡。這個時候主執行緒會釋放CPU資源進入休眠狀態,直到下個訊息到達或者有事務發生,通過往pipe管道寫端寫入資料來喚醒主執行緒工作。

Activity的生命週期都離不開訊息佇列

ActivityThread的內部類H繼承於Handler,在H.handleMessage(msg)方法中,根據接收到不同的msg,執行相應的生命週期。比如收到msg=H.LAUNCH_ACTIVITY,則呼叫ActivityThread.handleLaunchActivity()方法,最終會通過反射機制,建立Activity例項,然後再執行Activity.onCreate()等方法;再比如收到msg=H.PAUSE_ACTIVITY,則呼叫ActivityThread.handlePauseActivity()方法,最終會執行Activity.onPause()等方法。

主執行緒的訊息又是哪裡來的

App程序中的其他執行緒傳送訊息給主程序

圖解


一個暫停Activity的流程

AMS呼叫ATP

ATP通過Binder傳遞到AT

AT傳送訊息到主執行緒

主執行緒在Looper中無限迴圈地取,當收到暫停Activity的訊息,便將訊息分發給ActivityThread.H.handleMessage()方法。通過一系列方法的呼叫,最後會呼叫到onPause。onPause處理完後,繼續迴圈下去,或是阻塞。