十、Android的消息機制

從開發的角度來說,Handler是Android消息機制的上層接口,這使得在開發過程中只需要和Handler交互即可。
Handler的使用過程很簡單,通過它可以輕鬆地將一個任務切換到Handler所在的線程中去執行。
Android的消息機制主要指Handler的運行機制,Handler的運行需要底層的MessageQueue和Looper的支撐。

MessageQueue的中文翻譯是消息隊列,,它的內部存儲了一組消息,以隊列的形式對外提供插入和刪除的工作。雖然叫消息隊列,但是它的內部存儲結構並不是真正的隊列,而是採用單鏈表的數據結構來存儲消息列表。

Looper的中文翻譯爲循環,在這裏可以理解爲消息循環,由於MessageQueue只是一個消息的存儲單元,它不能去處理消息,而Looper就填補了這個功能,Looper會以無限循環的形式去查找是否有新消息,如果有的話就處理消息,否則就一直等待着。

Looper中還有一個特殊的概念,那就是ThreadLocal,ThreadLocal並不是線程,它的作用是可以在每個線程中存儲數據。我們知道,Handler創建的時候會採用當前線程的Looper來構造消息循環系統,那麼Handler內部如何獲取到當前的線程的Looper呢,這就要使用到ThreadLocal,ThreadLocal可以在不同的線程中互不干擾地存儲並提供數據,通過ThreadLocal可以輕鬆獲取每個線程的Looper。需要注意的是,線程默認是沒有Looper的,如果需要使用Handler就必須爲線程創建Looper。主線程ActivityThread,創建的時候會去初始化Looper,所以主線程默認可以使用Handler。

1.Android的消息機制概述

Android的消息機制主要是指Handler的運行機制,以及Handler所附帶的MessageQueue和Looper的工作過程,這三者實際上是一個整體,只不過我們在開發的過程中比較多地接觸到Handler而已。

Android爲什麼不允許在子線程中訪問UI呢?
這是因爲Android的UI控件不是線程安全的,如果在多線程中併發訪問可能會導致UI控件處於不可預期的狀態。
爲什麼系統不對UI控件的訪問加上鎖機制呢?
首先加上鎖機制會讓UI訪問的邏輯變得複雜,其次鎖機制會降低UI訪問的效率,因爲鎖機制會阻塞某些線程的執行。

Handler的工作原理:
Handler創建的時候回採用當前線程的Looper來構建內部的消息循環系統,如果當前線程沒有Looper,就會報錯;
Handler創建完畢後,這個時候其內部的Looper以及MessageQueue就可以和Handler一起協同工作了,然後通過Handler的post方法將一個Runnable投遞到Handler內部的Looper中去處理,也可以通過Handler的send方法發送一個消息,這個消息同樣會在Looper中去處理。其實post方法最終也是通過send方法來完成的。

Send方法的工作過程:
當Handler的send方法被調用時,它會調用MessageQueue的enqueueMessage方法將這個消息放入消息隊列中,然後Looper發現有新消息到來時,就會處理這個消息,最終消息中的Runnable或者Handler的handleMessage方法就會被調用。注意Looper是運行在創建Handler所在的線程中的,這樣一來Handler中的業務邏輯就被切換到創建Handler所在的線程中去了。

2.Android的消息機制分析

2.1.ThreadLocal的工作原理

ThreadLocal是一個線程內部的數據存儲類,通過它可以在指定的線程中存儲數據,數據存儲後,只有在指定的線程中可以獲取到存儲的數據,對於其他線程來說則無法獲取到數據。

當某些數據是以線程爲作用域並且不同線程具有不同的數據副本的時候,就可以考慮使用ThreadLocal。

2.2.消息隊列的工作原理

消息隊列在Android中指的是MessageQueue,MessageQueue主要包含兩個操作插入和讀取。
讀取操作本身會伴隨着刪除操作,插入和讀取對應的方法分別爲enqueueMessage和next,其中enqueueMessage是往消息隊列中插入一條信息,而next的作用是從消息隊列中取出一條信息並將其從消息隊列中移除。
儘管MessageQueue叫消息隊列,但是它的內部實現並不是用的隊列,實際上它是通過一個單鏈表的數據結構來維護消息列表,單鏈表在插入和刪除上面比較有優勢。

2.3.Looper的工作原理

Looper在Android的消息機制中扮演着消息循環的角色,具體來說就是它會不停地從MessageQueue中查看是否有新的消息,如果有新的消息,就會立刻處理,否則就會一直阻塞在那裏。
Handler的工作需要Looper,如果沒有Looper就會報錯,那麼如何爲一個線程創建Looper呢?
通過Looper.prepare()即可爲當前線程創建一個Looper,接着通過Looper.loop()來開啓消息循環。

new Thread("Thread2") {
  @override
  public void run() {
    Looper.prepare();
    Handler handler = new Handler();
    Looper.loop();
  }
}

Looper提供了quit和quitSafely來退出一個Looper,二者的區別是:
quit會直接退出Looper,而quitSafely只是設定一個退出標記,然後把消息隊列中的已有的消息處理完畢後才安全地退出。
Looper退出後,通過Handler發送的消息會失敗,這個時候Handler的send方法會返回false。

在子線程中,如果手動爲其創建了Looper,那麼在所有的事情完成以後應該調用quit方法來終止消息循環,否則,這個子線程就一直處於等待狀態,而如果退出Looper之後,這個線程就會立刻終止,因此建議在不需要的時候終止Looper。

2.4.Handler的工作原理

如果是post來的runnable直接執行,沒有的話,再去send的消息。

3.主線程的消息循環

Android的主線程就是ActivityThread,主線程的入口方法爲main,在main方法中系統會通過Looper.prepareMainLooper()來創建主線程的Looper以及MessageQueue,並通過Looper.loop()來開啓主線程的消息循環。
主線程的消息循環開始之後,ActivityThread還需要一個Handler來和消息隊列進行交互,這個Handler就是ActivityThread.H,它內部定義了一組消息類型,主要包含了四大組件的啓動和停止過程。

ActivityThread通過ApplicationThread和AMS進行進程間通信,AMS以進程間通信的方式完成ActivityThread的請求後會回調ApplicationThread中的Binder方法,然後ApplicationThread會向H發送消息,H收到消息後會將ApplicationThread中的邏輯切換到ActivityThread中去執行,即切換到主線程中去執行,這個過程就是主線程的消息循環。

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