什麼是 Handler?有什麼用?
在 android 應用開發中經常用到 Handler ,大概知道 Handler 常被用來處理線程間通信,比如:在子線程中處理某個邏輯後通過 Handler 發送消息的方式,切換回 UI 線程更新界面
Handler 到底是什麼呢?總能聽到最傻瓜式的問法,那麼就來簡單回答一下:Handler 不是一個具體的對象,暫且說是一套解決方案吧,在 Android 中處理消息 (Message)的一套方案。
我們通過研究 Handler 的運行機制來了解 android 的消息機制
什麼是 Handler 運行機制?
這裏需要引入幾個概念和名詞 Message
/ MessageQueue
/ Looper
以下請允許我用不太恰當地方式簡單解釋一下這幾個概念
Message :
消息
MessageQueue :
消息隊列
Looper :
無限循環地從隊列中獲取消息交給 Handler
Handler :
發送消息,處理消息
如果有人問你, Handler 運行機制 是怎麼樣的,你就告訴他:
Handler 發送消息(Message) 到消息隊列中(MessageQueue) ,Looper 通過無限循環的方式不斷向消息隊列中獲取新添加的消息 然後交給 Handler ,最終消息回到 Handler 中被處理
(平常開發中)由於 Handler 創建在主線程,處理消息的方法也運行在主線程,因此上述中消息被 Looper 交回給 Handler 的時候就實現了“線程切換”
喔~這個6,這個6,這個思想非常棒,仔細想想在子線程中做了一堆操作,什麼添加消息到隊列,我們不管,只認準最後 Looper 把消息交回給了 Handler ,又因爲 Handler 運行在主線程中,因此最後處理消息時就在主線程了(根本原因是創建Handler 的 Looper 默認使用主線程的 Looper )
理論往往是抽象枯燥的,我們總希望通過熟悉的代碼來慢慢體會,來看下日常開發中常用Handler使用場景,在子線程中處理耗時邏輯,然後再發送消息到主線程中更新UI
在主線程中創建Handler處理類
class MyHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
Log.e("tag", "thread name:" + Thread.currentThread().getName());
//更新UI
break;
}
}
}
MyHandler myHandler = new MyHandler();
創建線程,處理耗時邏輯,然後發送消息
new Thread("t1") {
@Override
public void run() {
//耗時操作
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Message message = Message.obtain();
message.what = 1;
//發送消息
myHandler.sendMessage(message);
}
}.start();
這裏可以看到,我們在主線程(activity運行在主線程中)創建Handler,然後創建一個子線程 t1 ,在 t1 中處理耗時邏輯,再通過 sendMessage() 發送消息,就是這麼一個簡單的代碼,能夠實現”線程切換”的效果,到底是怎麼實現的呢?
開發中我們能接觸到的只有 Handler ,對於 MessageQueue 和 Looper 這兩個概念比較陌生,就上述的例子中也完全沒有體現這些字眼,但是對於 Handler 的運行他們起到了不可缺少的重要作用。
Handler 通過 sendMessage 發送消息後,將 Message 發送到了哪裏呢? 又是怎麼拿到這些 Message 的呢?
MessageQueue 消息隊列工作原理
MessageQueue 翻譯爲消息隊列。簡單的說就是裝載消息(Message) 的容器。容器?那不是很簡單,只要負責 添加 和 讀取(移除) 內容就好了。
當 Handler 通過 sendMessage 方法發送 Message 時, MessageQueue 就會調用 enqueueMessage 方法把這條消息添加到自身容器中。然後呢?然後就放着唄,等着別人來取,當有人來取 Message 的時候,通過 next 方法取出一條消息並將消息從自身容器中移除,這就是消息隊列的工作職責
這裏注意,雖然叫做消息隊列,但是內部實現並不是用隊列,而是單鏈表的數據結構來維護消息列表
MessageQueue : 添加消息到隊列 enqueueMessage
上面說 sendMessage 時 MessageQueue 就會調用自身的 enqueueMessage
將消息添加到自身容器,怎麼實現的呢?Handler 和 MessageQueue 兩者又是怎麼關聯起來的?這時候我們就需要通過閱讀一下源碼啦
等一下,別走啊,這源碼很簡單的,不用一頭扎入,看個大概思路就行
我們知道 Handler 發送消息的方法有很多,先不管,需要知道的是最後調用的都是同一個 sendMessageAtTime()
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
這個就是 Handler 的 sendMessageAtTime
方法的源碼啦,很簡單,將一個 mQueue
(特別注意這個 mQueue)賦值給 MessageQueue
。 MessageQueue
如果爲空,拋異常,不爲空當做一個參數傳遞給 enqueueMessage
(Handler 的方法),恩,這裏問題不大,很普通的寫法,接着看看 Handler 的 enqueueMessage
方法
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
丟掉前面無關的代碼,直接聚焦 queue.enqueueMessage(msg, uptimeMillis)
這行代表表示,最終調用了 MessageQueue
的 enqueueMessage
方法將消息添加進隊列
到這裏已經很清晰可以看到 MessageQueue 將消息加入隊列的邏輯了,不用看都知道, enqueueMessage
具體實現肯定是操作鏈表添加內容
怎麼樣?是不是很簡單,很清晰的看到
Handler 調用 sendMessage 方法時 MessageQueue 會通過 enqueueMessage 方法將 message 添加到自身容器中
嗯,細心的同學或許就會問了,這個 MessageQueue
是哪裏來的,你突然引入一個 MessageQueue
然後就開始添加 消息了,也沒說他是哪裏崩出來的呀
回到剛纔那個 sendMessageAtTime()
方法,第一行代碼出現:MessageQueue queue = mQueue;
原來 MessageQueue
是通過 mQueue
賦值的。
在 Handler 類中搜索 mQueue
,找啊找啊,最後在 Handler 構造方法中找到它
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
沒錯就是它 mQueue
,它是通過 looper.mQueue
得到的
而且我們通過其他的構造函數發現 Handler 的所有構造函數最終都是調用這個方法,也就是說,我們在創建 Handler 的時候就創建了這個 MessageQueue
soga 原來 MessageQueue
是通過 Looper
得到的,那 Looper
又是什麼?乘勝追擊看看這個 Looper
是什麼。
Looper 工作原理
Looper
在 android 消息機制中扮演着 循環從隊列中獲取消息的角色,就是說它會無限循環的查詢 消息隊列中是否有新的消息,如果有就獲取到這個消息,然後交給Handler,如果沒有就阻塞在那裏
先看看 Looper
的構造方法
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
在 Looper 構造方法中,創建了一個 MessageQueue 即消息隊列,然後獲取當前線程保存起來。
我們知道 Handler 工作需要 Looper 的支持,如果沒有 Looper 的線程中使用 Handler 就會報錯,那如何爲一個線程創建一個 Looper 呢? 很簡單, 通過 Looper.prepure()
就可以爲一個線程創建 Looper ,接着調用 Looper.loop()
來開啓消息循環。示例如下
new Thread("t1") {
@Override
public void run() {
Looper.prepare();//創建Looper
Handler handler = new Handler();//Handler 使用
Looper.loop();//開啓消息循環
}
}.start();
Looper.prepare()
創建 Looper 很簡單不說了,下面有個 Looper.loop()
開啓消息循環,這個是 Looper 的重心啦,看看怎麼實現的
loop()
的源碼比較多,不必每一行都去分析,我們通過剔除一些代碼,讓源碼看起來簡潔一些,這樣就不會掉進源碼的漩渦中不能自拔。
public static void loop() {
//剔除代碼
final MessageQueue queue = me.mQueue;
//...
for (;;) {
Message msg = queue.next(); // 調用 MessageQueue 的 next 方法獲取 Message
if (msg == null) {
// No message indicates that the message queue is quitting.
// msg == null 表示 MessageQueue 正在退出(調用了quit等方法)
return;
}
//剔除代碼
try {
// msg.target 就是發送消息的Handler,因此這裏將消息交回 Handler
msg.target.dispatchMessage(msg);
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
//剔除代碼
}
}
前面說了,Looper 通過無限循環的方式獲取 消息隊列 中的消息
for (;;)
死循環從消息隊列中獲取 消息 ,當 msg == null
跳出循環。
通過調用 Looper 的 quit()
或者 quitSafely()
讓 Looper 退出循環
// quit
public void quit() {
mQueue.quit(false);
}
// quitSafely()
public void quitSafely() {
mQueue.quit(true);
}
我們看到 Looper 的 quit()
或者 quitSafely()
方法調用的是 MessageQueue
的 quit()
方法,使得 msg 返回 null 從而讓 Looper 退出循環。這兩個方法的區別是: quit 是直接退出 Looper; quitSafely 會設置一個退出標識,等到隊列中的消息都處理完了,再安全退出。Looper 退出後 Handler 就無法發送消息了
loop() 中 獲取到消息通過 msg.target.dispatchMessage(msg)
將消息 message 交給 Handler 的 dispatchMessage
方法處理,這也就是爲什麼 能夠實現線程切換的關鍵所在,因爲 Handler 運行在主線程
MessageQueue : 獲取消息 next
在 Looper.loop()
中我們看到通過 MessageQueue
的 next
方法獲取隊列中的消息
Message next() {
// 剔除代碼
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
// 阻塞
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
//存在消息,未到執行時間
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
//存在消息,獲取消息返回
// Got a message.
mBlocked = false;//表示目前沒有阻塞
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;//將 msg 從 MessageQueue 鏈表中移除
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// 沒有消息處理 進入阻塞
// No more messages.
nextPollTimeoutMillis = -1;
}
// MessageQueue 退出 返回 null
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
//剔除代碼
}
//剔除代碼
}
}
next
通過 for (;;)
死循環不斷獲取新增的消息,返回給 Looper,如果沒有消息,就一直阻塞在這裏,當有新消息到達,next
會返回這條消息,並且從鏈表中刪除
Handler 工作原理
Handler 的工作原理就比較簡單啦,主要負責 發送消息 和 處理消息
發送消息 主要通過一些列 post
和 send
方法來實現,具體可以查查接口文檔,通過源碼可以看到最終都是調用 sendMessageAtTime()
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
sendMessageAtTime()
的源碼很簡單,調用 MessageQueue
的方法 enqueueMessage
將 Message 添加到消息隊列,同時 msg.target = this;
將 Handler
自身賦值給了 msg.target
可以發現, Handler 發送的消息過程是向消息隊列添加一條消息,這時 MessageQueue 的 next 方法就會將這條消息返回給 Looper,Looper 收到消息後開始處理,最終通過 msg.target.dispatchMessage(msg);
將消息交給 Handler ,即 Handler 的 dispatchMessage
方法會被調用,這時 Handler 就進入了消息處理階段
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
Handler 處理消息的過程:
首先檢查 Message
的 callback
是否爲 null。 不爲 null 直接通過 handleCallback
來處理消息。Messsage
的 callback
是一個 Runnable
對象,也就是 Handler 的 post 系列方法所傳遞的 Runnable 參數。 handler.post(runnable);
handleCallback
private static void handleCallback(Message message) {
message.callback.run();
}
然後檢查 mCallback
是否 null。不爲 null 直接通過 mCallback
的 handleMessage
處理消息。mCallback
是一個接口,定義如下
/**
* Callback interface you can use when instantiating a Handler to avoid
* having to implement your own subclass of Handler.
* //不需要通過一個 Handler 的子類,就可以創建一個 Handler 的實例
*
* @param msg A {@link android.os.Message Message} object
* @return True if no further handling is desired
*/
public interface Callback {
public boolean handleMessage(Message msg);
}
接口註釋說明了,通過 Callback
可以不通過 Handler 的子類就能實現一個 Handler 的實例。
不理解?沒事,代碼解釋。開發日常我們用的比較多的是通過繼承 Handler 實現一個 Handler 的子類來實現
class MyHandler extends Handler {
@Override
public void handleMessage(Message msg) {
//處理消息
}
}
而使用 Callback
接口時
Handler handler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
return false;
}
});
當我們不想派生子類時,就可以通過 Callback
的方式實現
OK,再回過頭來看看 Handler 運行在主線程 這個問題。
其實 Handler 運行在哪個線程,是由創建 Handler 的 Looper 決定的,簡單看個例子(onCreate()方法中)
new Thread("t1") {
@Override
public void run() {
Looper.prepare();
looper = Looper.myLooper();
Looper.loop();
}
}.start();
SystemClock.sleep(100);//確保成功獲取到 looper
Handler handler = new Handler(looper, new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
Log.e("Ruffian", "Thread Name:" + Thread.currentThread().getName());
return false;
}
});
Message mess = Message.obtain();
mess.what = 1;
handler.sendMessage(mess);
解釋一下代碼:創建一個子線程 t1 ,爲這個線程創建 Looper (Looper.prepare();
),然後獲取 t1 線程的 Looper 用於創建 Handler
在 Handler 的 handleMessage 中處理消息,打印 Handler 所在的線程,結果爲: t1
那爲什麼默認創建的 Handler 就是運行在主線程的?看下面的例子
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Message mess = Message.obtain();
mess.what = 1;
Handler handler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
Log.e("Ruffian", "Thread Name:" + Thread.currentThread().getName());
return false;
}
});
handler.sendMessage(mess);
}
在 Handler 的 handleMessage 中處理消息,打印 Handler 所在的線程,此時,結果爲: main
看一下 Handler 構造方法的代碼(在不指定Looper的時候)
public Handler(Callback callback, boolean async) {
//剔除代碼
mLooper = Looper.myLooper();//獲取當前線程Looper
//剔除代碼
}
通過代碼可以看出,在創建 Handler 不指定 Looper 時,Handler 的構造方法會獲取當前線程的 Looper ,由於activity 的 onCreate()
方法運行在主線程,所以 Looper 也屬於主線程,通過此 Looper 創建的 Handler 當然也運行在主線程啦~
由此可以驗證 Handler 運行在哪個線程由 創建 Handler 的 Looper 屬於哪個線程決定
講完…