一、Handler的作用
- 官方解釋
Handler允許你發送和處理Message和與線程的MessageQueue關聯的Runnable對象。每個Handler實例與一個線程和它的MessageQueue相關聯。當你創建一個新的Handler,它就被綁定到這個線程或者這個線程的消息隊列上–從那時起,它會傳遞messages和runnables到那個消息隊列,然後當他們離開消息隊列時執行他們。 - 個人理解
Handler是一個線程消息處理者,是UI線程和子線程(工作線程)之間的信使。他可以將工作線程中需更新UI的操作信息 傳遞到 UI主線程,從而實現在工作線程對UI的異步更新。
ps:其實Handler可以完成任何線程之間的消息傳遞,不只是主線程與子線程。
二、爲啥用Handler
- 在多個線程同時更新UI的時候,保證線程的安全。
- 假如最初的設計沒有Handler,而是所有地方都能更新UI,那麼我有很多個線程同時在給一個文本控件setText,那肯定會引起界面混亂。如果這時候要解決怎麼辦?加鎖?這樣又會影響性能。
- 所以Android在設計的時候就加入了這個Handler的機制讓我們去遵守而不用去考慮多線程的問題。
三、Handler的機制(原理)
一句話解釋:
Handler通過執行其綁定線程的消息隊列(MessageQueue)中不斷被Looper循環取出的消息(Message)來完成線程間的通信。
主要有幾個類需要理解:
1. Looper
- 官方解釋
用於運行線程的消息循環的類。默認情況下線程沒有與之關聯的消息循環;在要運行循環的線程中調用prepare來創建一個Looper,然後調用loop使其循環處理消息,直到循環結束爲止。 - 主要方法
首先看prepare方法:
/**
* 將當前線程初始化爲Looper,這使您有機會創建Handlers,然後在實際開始循環之前先引用這個Looper。
* 確保在調用此方法後調用{@link #loop()},並通過調用{@link #quit()}結束該方法。
*/
public static void prepare() {
prepare(true);
}
/**
* 這裏sThreadLocal是一個ThreadLocal的對象,可以在一個線程中存儲變量。
* 這裏可以看到儲存的是一個Looper的實例,前三行判斷如果sThreadLocal.get()爲空,則拋出異常。
* 說明此方法不能被調用兩次,也保證了一個線程中只有一個Loopper實例。
*/
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
然後看最後一行中實例化Looper的構造方法:
/**
* 實例化了一個消息隊列(MessageQueue)對象和將當前線程賦值給一個全局變量mThread
*/
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
再看loop方法,只保留了重點代碼:
/**
*在此線程中運行消息隊列。 確保調用{@link #quit()}以結束循環
*/
public static void loop() {
// 調用sThreadLocal.get()方法獲取之前儲存的Looper實例
final Looper me = myLooper();
if (me == null) {
// 如果me爲null則拋出異常,也就是說loop方法必須在prepare方法之後運行。
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
// 拿到該looper實例中的mQueue(消息隊列)
final MessageQueue queue = me.mQueue;
// 開啓無限循環
for (;;) {
// 取出一條消息,如果沒有消息則阻塞
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
try {
// 使用調用 msg.target.dispatchMessage(msg);把消息交給msg的target的dispatchMessage方法去處理。
//Msg的target就是handler對象
msg.target.dispatchMessage(msg);
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
//釋放消息佔據的資源
msg.recycleUnchecked();
}
接下來就走到了Handler的dispatchMessage方法了
2. Handler
- 先看構造方法,通過as的structure可以看到Handler有7個構造方法,:
這裏我們平時使用最多的可能是兩種,一個是無參的,一種是傳入Looper對象的。但是不管哪種,最終都是走到了最後兩個構造方法。來看倒數第二個方法:
public Handler(Callback callback, boolean async) {
// 這個FIND_POTENTIAL_LEAKS變量恆爲false,看不懂
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
// 獲取當前線程保存的Looper實例
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
// 獲取當前線程保存的Looper實例中實例化時保存的MessageQueue(消息隊列)
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
這個構造方法保證了handler的實例與我們Looper實例中MessageQueue關聯上了。
最後一種更粗暴,直接把需要的變量都傳進去了,全部賦值:
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
總之,不管以哪種方式實例化Handler,目的都是要將其與MessageQueue關聯上。
- sendMessage,這個方法平時是用的最多的了
public final boolean sendMessage(Message msg){
return sendMessageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
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方法將其傳入。
在enqueueMessage方法中把Message.target 賦值爲this(在上面Looper的loop方法裏面就是利用了這個target.dispatchMessage(msg)來傳遞的消息),也就是將當前的Handler實例傳入Message賦值給其target屬性;最後調用queue.enqueueMessage方法把消息保存到消息隊列中。
說到這裏,倒是可以看看這個dispatchMessage方法了:
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
// 這裏mCallback是否爲空取決於調用哪個構造函數,但是目的都是爲了讓我們執行handleMessage方法,不管是Handler的還是Callback
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
最後來看handleMessage方法:
public interface Callback {
/**
* @param msg A {@link android.os.Message Message} object
* @return True if no further handling is desired
*/
public boolean handleMessage(Message msg);
}
/**
* Subclasses must implement this to receive messages.
*/
public void handleMessage(Message msg) {
}
可以看到不管是不管是Handler的還是Callback的handleMessage方法,都是需要我們自己去重寫然後根據回調的msg進行消息處理。
- 總結Handler機制
- 1.Looper.prepare()在當前線程使用ThreadLocal保存一個通過構造方法生成的Looper實例,並保存一個MessageQueue實例。該方法在同一個線程只能調用一次,所以一個線程只會存在一個Looper和一個MessageQueue。
- 2.Looper.loop()開啓無限循環,每次從MessageQueue實例取出一條消息,再通過該消息的 msg.target.dispatchMessage(msg) 方法將消息進行傳遞。
- 3.通過Handler的構造方法獲取當前線程中的Looper實例及獲取當該Looper對象中實例化時保存的MessageQueue(消息隊列)。
- 4.通過handler.sendMessage(Message msg)給msg的target賦值爲handler自身,然後加入MessageQueue中。
- 5.msg.target.dispatchMessage(msg) 調用到最後實際是回調了Handler的handleMessage方法,由我們自行重寫處理消息。
四、Handler的用法
常用有兩種
- 直接new Handler(),通過handler.sendMessage()發消息,然後重寫handleMessage()方法等待回調處理消息。
Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
logic();
}
};
- 傳入Looper,此處爲封裝主線程,傳入Looper.getMainLooper(),保證了不管在什麼線程中實例化,都是在主線程回調。然後調用handler.post(Runnable r)方法或postDelayed。
ps:在主線程中創建Handler時,主線程會自動創建Looper的,而自己在子線程中自定義handler時,需要手動創建Looper,並調用Looper的prepare方法,或者傳Looper.getMainLooper()
public class MainThread {
private Handler mHandler;
public MainThread() {
mHandler = new Handler(Looper.getMainLooper());
}
public void post(Runnable runnable) {
postDelayed(runnable, 0);
}
public void postDelayed(Runnable runnable, long delayMillis) {
mHandler.postDelayed(runnable, delayMillis);
}
public void removeCallbacks(Runnable runnable) {
mHandler.removeCallbacks(runnable);
}
}
- 子線程創建Handler,如果不給Handler指定一個Looper就會出現異常
class MyThread extends Thread{
public Handler handler;
@Override
public void run() {
Looper.prepare(); //創建一個Looper對象
handler = new Handler(){
//此方法運行在子線程中
@Override
public void handleMessage(Message msg) {
System.out.println("當前的線程-->" + Thread.currentThread());
}
};
Looper.loop(); //開始輪詢
}
}
MyThread thread = new MyThread();
thread.start(); //啓動線程
//發送消息就會執行該handler的handleMessage方法(在子線程中)
thread.handler.sendEmptyMessage(1);
五、用到Handler的地方
- HandlerThread
本身是一個子線程,已經在run()方法中指定了Looper,可以直接進行異步操作。
- Activity的runOnUiThread(Runnable action)方法,實際上是用了handler.post(action)方式。內部先判斷當前線程是不是主線程,如果不是就通過handler發送一個消息。
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
}
- Activity的啓動流程中(在ActivityThread的main方法裏面調用Looper.prepareMainLooper())
給sMainLooper賦值當前線程(即主線程)的Looper。