從開發的角度來說,Handler是Android消息機制的上層接口。Handler的運行需要底層的 MessageQueue 和 Looper 的支撐。
- MessageQueue是一個消息隊列,內部存儲了一組消息,以隊列的形式對外提供插入和刪除的工作,內部採用單鏈表的數據結構來存儲消息列表。
- Lopper會以無限循環的形式去查找是否有新消息,如果有就處理消息,否則就一直等待着。 線程是默認沒有Looperd的,使用Handler就必須爲線程創建Looper。
- ThreadLocal可以在不同線程中互不干擾的存儲並提供數據,通過ThreadLocal可以輕鬆的獲取每個線程的Looper。
我們經常提到的主線程,也叫UI線程,它就是ActivityThread
Android消息機制概述
- Handler的主要作用是將某個任務切換到Handler所在的線程中去執行。爲什麼Android要提供這個功能呢?這是因爲Android規定訪問UI只能通過主線程,如果子線程訪問UI,程序會拋出異常;ViewRootImpl在checkThread方法中做了判斷。
- 由於Android不建議在主線程進行耗時操作,否則可能會導致ANR。那我們耗時操作在子線程執行完畢後,我們需要將一些更新UI的操作切換到主線程當中去。所以系統就提供了Handler。
- 系統爲什麼不允許在子線程中去訪問UI呢?因爲Android的UI控件不是線程安全的,多線程併發訪問可能會導致UI控件處於不可預期的狀態,爲什麼不加鎖?因爲加鎖機制會讓UI訪問邏輯變得複雜;其次鎖機制會降低UI訪問的效率,因爲鎖機制會阻塞某些線程的執行。所以Android採用了高效的單線程模型來處理UI操作。
- Handler創建時會採用當前線程的Looper來構建內部的消息循環系統,如果當前線程沒有Looper就會報錯。Handler可以通過post方法發送一個Runnable到消息隊列中,也可以通過send方法發送一個消息到消息隊列中,其實post方法最終也是通過send方法來完成。
- MessageQueue的enqueueMessage方法最終將這個消息放到消息隊列中,當Looper發現有新消息到來時,處理這個消息,最終消息中的Runnable或者Handler的handleMessage方法就會被調用,注意Looper是運行Handler所在的線程中的,這樣一來業務邏輯就切換到了Handler所在的線程中去執行了。
Android的消息機制分析
ThreadLocal的工作原理
ThreadLocal是一個線程內部的數據存儲類,通過它可以在指定線程存儲數據,數據存儲後,只能在指定的線程可以獲取到存儲的數據,對於其他線程則無法獲取到數據。一般來說,當數據是以線程作爲作用域並且不同線程有不同副本的時候,就可以考慮使用ThreadLocal。對於Handler來說,它需要獲取當前線程的Looper,而Looper的作用於就是線程並且不同的線程具有不同的Looper,通過ThreadLocal可以輕鬆實現線程中的存取。
ThreadLocal的另一個使用場景是複雜邏輯下的對象傳遞。
ThreadLocal原理:不同線程訪問同一個ThreadLoacl的get方法,ThreadLocal的get方法會從各自的線程中取出一個數組,然後再從數組中根據當前ThreadLocal的索引去查找對應的Value值。
ThreadLocal set方法
public void set(T value) {
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);//通過values方法獲取當前線程中的ThreadLoacl數據——localValues
if (values == null) {
values = initializeValues(currentThread);
}
values.put(this, value);
}
在 localValues 內部有一個數組: private Object[] table ,ThreadLocal的值就存在這 個數組中。
ThreadLocal的值在table數組中的存儲位置總是ThreadLocal的reference字段所標識的對象的下一個位置。
ThreadLocal的get方法:
public T get() {
// Optimized for the fast path.
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);//找到localValues對象
if (values != null) {
Object[] table = values.table;
int index = hash & values.mask;
if (this.reference == table[index]) {//找到ThreadLocal的reference對象在table數組中的位置
return (T) table[index + 1];//reference字段所標識的對象的下一個位置就是ThreadLocal的值
}
} else {
values = initializeValues(currentThread);
}
return (T) values.getAfterMiss(this);
}
從ThreadLocal的set/get方法可以看出,它們所操作的對象都是當前線程的localValues對象 的table數組,因此在不同線程中訪問同一個ThreadLocal的set/get方法,它們對ThreadLocal 的讀/寫操作僅限於各自線程的內部。
消息隊列的工作原理
-
消息隊列指的是MessageQueue,主要包含兩個操作:插入和讀取。讀取操作本身會伴隨着刪除操作。
-
MessageQueue內部通過一個單鏈表的數據結構來維護消息列表,這種數據結構在插入 和刪除上的性能比較有優勢。
-
插入和讀取對應的方法分別是: enqueueMessage 和 next 方法。
i. enqueueMessage()的源碼實現主要操作就是單鏈表的插入操作
ii. next()的源碼實現也是從單鏈表中取出一個元素的操作,next()方法是一個無限循環的方法,如果消息隊列中沒有消息,那麼next方法會一直阻塞在這裏。當有新消息到 來時,next()方法會返回這條消息並將其從單鏈表中移除。
Looper的工作原理
- Looper在Android的消息機制中扮演着消息循環的角色,具體來說就是它會不停地從 MessageQueue中查看是否有新消息,如果有新消息就會立即處理,否則就一直阻塞在那裏。
- 通過 Looper.prepare() 方法即可爲當前線程創建一個Looper,再通過 Looper.loop() 開啓消息循環。 prepareMainLooper() 方法主要給主線程也就是ActivityThread創建Looper使用的,本質也是通過prepare方法實現的。
- Looper提供quit和quitSafely來退出一個Looper,區別在於quit會直接退出Looper,而 quitSafely會把消息隊列中已有的消息處理完畢後才安全地退出。 Looper退出後,這時候 通過Handler發送的消息會失敗,Handler的send方法會返回false。
- 在子線程中,如果手動爲其創建了Looper,在所有事情做完後,應該調用Looper的quit方法來終止消息循環,否則這個子線程就會一直處於等待狀態;而如果退出了Looper以 後,這個線程就會立刻終止,因此建議不需要的時候終止Looper。
- loop()方法會調用MessageQueue的 next() 方法來獲取新消息,而next是是一個阻塞操作,沒有信息時,next方法會一直阻塞在那裏,這也導致loop方法一直阻塞在那裏。如果MessageQueue的next方法返回了新消息,Looper就會處理這條消息: msg.target.dispatchMessage(msg) ,這裏的msg.target是發送這條消息的Handler對象,這樣Handler發送的消息最終又交給Handler來處理了。
Handler的工作原理
Handler的工作主要包含消息的發送和接受過程。
發送過程通過post的一系列方法和send的一系列方法來實現。Handler發送過程僅僅是向消息隊列中插入了一條消息。MessageQueue的next方法就會返回這條消息給Looper,Looper拿到這條消息就開始處理,最終消息會交給Handler的dispatchMessage()來處理,這時Handler就進入了處理消息的階段。dispatchMessage()方法運行在Looper所在的線程上。
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
//Message的callback是一個Runnable,
//也就是Handler的 post方法所傳遞的Runnable參數
handleCallback(msg);
} else {
//如果給Handler設置了Callback的實現,
//則調用Callback的handleMessage(msg)
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
//調用Handler的handleMessage方法來處理消息,
//該Handler子類需重寫handlerMessage(msg)方法
handleMessage(msg);
}
}
private static void handleCallback(Message message) {
message.callback.run();//直接執行Runnable中的run()方法
}
public interface Callback {
//不想繼承Handler子類,可以通過Callback來實現handleMessage
public boolean handleMessage(Message msg);
}
//默認空實現
public void handleMessage(Message msg) {
}
Handler還有一個特殊的構造方法,可以指定一個特殊的Looper來構造Handler。
public Handler(Looper looper) {
this(looper, null, false);
}
Handler創建需要Looper,否則會拋出異常,默認獲取當前線程的Looper。主線程也就是ActivityThread會自動創建Looper,其他線程如果需要Looper均需要手動創建。
主線程消息循環
Android的主線程就是ActivityThread,主線程的入口方法爲main,在main方法中系統會通過Looper.prepareMainLooper()來創建主線程的Looper以及MessageQueue,並通過Looper.loop()來開啓主線程的消息循環。
public static void main(String[] args) {
...
Process.setArgV0("<pre-initialized>");
Looper.prepareMainLooper();//創建主線程的Looper
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
AsyncTask.init();
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
Looper.loop();//開啓looper
throw new RuntimeException("Main thread loop unexpectedly exited");
}
主線程的消息循環開始後,ActivityThread還需要一個Handler來和消息隊列進行交互,這 個Handler就是ActivityTread.H,它內部定義了一組消息類型,主要包含四大組件的啓動和停止過程。
Android主線程的消息循環模型
ActivityThread通過ApplicationThread和AMS進行進程間通信,AMS以進程間通信的方式完成 ActivityThread的請求後會回調ApplicationThread中的Binder方法,然後ApplicationThread會向H發送消息,H收到消息後會將ApplicationThread中的邏輯切換到ActivityTread中去執行, 即切換到主線程中去執行。四大組件的啓動過程基本上都是這個流程。