Android——主線程的loop爲什麼不會造成死循環?

線程是一段可執行的代碼,當可執行代碼執行完成後,線程生命週期便該終止了,線程退出。而對於主線程,我們是絕不希望會被運行一段時間,自己就退出,那麼如何保證能一直存活呢?簡單做法就是可執行代碼是能一直執行下去的,死循環便能保證不會被退出。

MainThread的loop爲什麼不會造成死循環?
因爲這個loop處理一切的事件,包括繪製,點擊事件等等,它停止了,那整個android程序就退出了。

但這裏可能又引發了另一個問題,既然是死循環又如何去處理其他事務呢?通過創建新線程的方式。(這裏的其他事務指的是什麼?後面會講到)

我們先看一下ActivityThread的main方法:

public static void main(String[] args) {
        ...
        //創建Looper和MessageQueue對象,用於處理主線程的消息
        Looper.prepareMainLooper();

        //創建ActivityThread對象
        ActivityThread thread = new ActivityThread();

        //建立Binder通道 (創建新線程)
        thread.attach(false);

        //消息循環運行
        Looper.loop();
        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

thread.attach(false);便會創建一個Binder線程(具體是指ApplicationThread,Binder的服務端,用於接收系統服務AMS發送來的事件),該Binder線程通過Handler將Message發送給主線程

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進程中的其他線程通過Handler發送給主線程:

圖片來源:https://www.zhihu.com/question/34652589/answer/90344494

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,流程如下:

  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下去。

一個線程可能有一個Looper,它包含一個MessageQueue。爲了使用這個功能,您必須通過在當前線程上調用(靜態的)Looper.prepare()創建一個Looper.prepare(),然後通過調用(同樣是靜態的)Looper.loop()來啓動循環。這些是靜態的,因爲每個線程應該只有一個Looper。


對loop()的調用通常不會返回,它會從MessageQueue中獲取消息並單獨處理它們(例如,調用Message中包含的Runnable)。當隊列中沒有剩餘的消息時,線程將阻塞,直到有新的消息爲止。要停止Looper,您必須在它上調用quit()(它可能不會立即停止循環,而是設置一個私有標誌,該標誌定期從循環中檢查,向Looper發出停止的信號)。

 

在某些時候(可能在創建任何活動或類似的東西之前),framework已經設置了一個Looper(包含MessageQueue)並啓動了它。從那時候開始,UI線程上發生的所有事情都通過這個循環。這包括活動生命週期管理等等。覆蓋(onCreate()、onDestroy()…)的所有回調都至少是間接地從循環中發出的。您可以在異常的堆棧跟蹤中看到這一點。

 

實際上,在主線程中的Looper是允許繪圖的。當視圖無效(例如,調用invalidate)時,將向主Looper發送一條消息,告訴它請求重繪。當Looper處理該消息時,重繪就發生。


爲什麼不立即繪製而是發送消息呢?答案是性能。繪畫是緩慢的。如果您對一個事件做了多個更改,您不希望爲每個更改重新繪製屏幕。如果用發送消息的機制,就可以將所有需要重新繪製的事件打包成一個重新繪製的事件。例如,如果您設置一個視圖的文本和另一個視圖的圖像,它們將同時被重繪,而且只會被重繪一次,而不是兩次。

 

關於IdleHandler:https://blog.csdn.net/tencent_bugly/article/details/78395717

借鑑:https://www.zhihu.com/question/34652589/answer/90344494

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