目錄

1. Handler的作用
簡單說Handler用于同一個進程的線程之間通信。(可以是不同線程,也可以是同一個線程,比如像做延時操作)
基本原理是,Handler發(fā)送Message,并放入對應線程的MessageQueue中,Looper讓對應線程無限循環(huán)地從自己的MessageQueue拿出消息處理。(Handler和Looper持有的是同一個MessageQueue)
使用的最多的場景就是,我們在UI線程創(chuàng)建好Handler實例,然后在子線程做完耗時操作后,想要更新UI內(nèi)容時,通過mHander sendMessage通知UI更新。而正如1所說,理論上我們也完全可以由UI線程發(fā)送Message,由子線程接收并處理,只是比較少見。另一個多見的場景是延時任務,往往是UI線程自己跟自己通信。
2. 為什么需要Handler?
一般來說,我們只要在子線程把信息放進主線程的MessageQueue里就可以了。因為,在同一進程中線程和線程之間資源是共享的,也就是對于任何變量在任何線程都是可以訪問和修改的,只要考慮并發(fā)性做好同步就行了,那么只要拿到主線程的MessageQueue 的實例,就可以放入消息,主線程的Looper在輪詢MessageQueue時,就可以取出該消息并處理。
主線程的MessageQueue的實例是可以拿到的(在主線程下 Looper.myLooper().mQueue),但是Google 為了統(tǒng)一添加消息和消息的回調(diào)處理,又專門構建了Handler類.只要在主線程構建Handler類,那么這個Handler實例就獲取主線程MessageQueue實例的引用,Handler 在sendMessage的時候就通過這個引用往消息隊列里插入新消息。
Handler 的另外一個作用,就是能統(tǒng)一處理消息的回調(diào)。這樣一個Handler發(fā)出消息又確保消息處理也是自己來做,這樣的設計非常的贊。具體做法就是在隊列里面的Message持有Handler的引用(哪個handler 把它放到隊列里,message就持有了這個handler的引用),然后等到主線程輪詢到這個message的時候,就來回調(diào)我們經(jīng)常重寫的Handler的handleMessage(Message msg)方法。
// Looper.loop輪詢方法
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
// 獲取MessageQueue
final MessageQueue queue = me.mQueue;
// 省略
// 具體的輪詢邏輯,無限for循環(huán)
for (;;) {
// 取出Message
Message msg = queue.next(); // might block
// 省略
try {
// target為發(fā)送message的Handler實例
// Handler處理
msg.target.dispatchMessage(msg);
}
// 省略
}
}
所以說,引入Handler只是為了大家使用方便以及代碼的清晰簡潔。并沒有大家想的那么高深。
3. 具體的使用
3.1 主線程使用Handler刷新UI
Handler handler = new Handler()
實際會調(diào)用
public Handler(Callback callback, boolean async) {
// 省略
// 這里也驗證了,Handler在哪個線程創(chuàng)建,他就會持有哪個線程的Looper
// 我們一般在UI線程初始化,Handler就會持有UI線程的Looper和MessageQueue
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
// 這里也驗證了,Handler在哪個線程創(chuàng)建,他就會持有哪個線程的MessageQueue
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
在其他線程創(chuàng)建想在主線程處理事情的Handler,可以用以下代碼可以達到相應效果
// 傳入UI線程的Looper
Handler handler = new Handler(Looper.getMainLooper);
3.2 LooperThread子線程使用(官網(wǎng)的文檔)
class LooperThread extends Thread {
//其他線程可以通過mHandler這個引用給該線程的消息隊列添加消息
public Handler mHandler;
public void run() {
Looper.prepare();
//需要在線程進入死循環(huán)之前,創(chuàng)建一個Handler實例供外界線程給自己發(fā)消息
mHandler = new Handler() {
public void handleMessage(Message msg) {
//Handler 對象在這個線程構建,那么handleMessage的方法就在這個線程執(zhí)行
}
};
// loop方法里會用到Handler實例
// 所以必須先初始化Handler
// 如果在loop方法之后初始化Handler,那么loop方法執(zhí)行中會報錯
Looper.loop();
// loop之后才初始化Handler,代碼是無效的,loop是死循環(huán),正常情況下這行代碼就不會執(zhí)行了
// mHandler = new Handler()......
}
}
需要說明的是,上面寫到的Looper.prepare,創(chuàng)建Handler和Looper.loop方法的順序并不一定不能改。如果你想的話,也完全可以loop執(zhí)行之后創(chuàng)建Handler,只是創(chuàng)建的流程不能寫在loop后面。因為loop里的死循環(huán)會導致你的代碼不執(zhí)行,你可以在主線程通過LooperThread.mHander這樣的引用,來創(chuàng)建實例,效果也是一樣的。
注意,其實UI線程也有類似的代碼,如下:
public final class ActivityThread {
public static final void main(String[] args) {
......
Looper.prepareMainLooper();
......
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
......
Looper.loop();
......
}
}
與上面的例子類似,系統(tǒng)在這里也為我們初始化了一個Handler。我們每次使用Handler mHandler = new Handler();都是額外創(chuàng)建了一個Handler,與原有的不沖突。msg.target.dispatchMessage(msg)這句代碼會判斷target。
4.Handler發(fā)送消息的兩種方式
Handler,它直接繼承自Object,一個Handler允許發(fā)送和處理Runnable或者Message對象,并且會關聯(lián)到主線程的MessageQueue中。所以Handler把消息壓入MessageQueue也有兩種方式,post(new Runnable)和sendMessage(Message msg)。
4.1 post
post允許把一個Runnable對象入隊到消息隊列中。它的方法有:
- post(Runnable)
- postAtTime(Runnable,long)
- postDelayed(Runnable,long)。
4.2 sendMessage
sendMessage允許把一個包含消息數(shù)據(jù)的Message對象壓入到消息隊列中。它的方法有:
- sendEmptyMessage(int)
- sendMessage(Message)
- sendMessageAtTime(Message,long)
- sendMessageDelayed(Message,long)。
從上面的各種方法可以看出,不管是post還是sendMessage都具有多種方法,它們可以設定Runnable對象和Message對象被入隊到消息隊列中,是立即執(zhí)行還是延遲執(zhí)行。
4.3 post和sendMessage方法的聯(lián)系和區(qū)別
先看源碼
public final boolean post(Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
代碼很好理解,post方法其實還是把Runnable對象轉(zhuǎn)化成了Message,區(qū)別在于普通的sendMessage不會使用callBack參數(shù),它具體的處理邏輯在Handler的handleMessage里。而post會使用Message的callback,callback就是Runnable對象,所以使用post方法的話,無需再去寫具體的handleMessage邏輯。源碼如下:
// dispatchMessage方法是在Looper.loop開啟循環(huán),開始處理MessageQueue里的每個Message時調(diào)用的,可以發(fā)現(xiàn),默認先調(diào)用callback,沒有callback才會使用handleMessage
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
5. 了解Message
Message是一個final類,所以不可被繼承。Message封裝了線程中傳遞的消息,如果對于一般的數(shù)據(jù),Message提供了getData()和setData()方法來獲取與設置數(shù)據(jù),其中操作的數(shù)據(jù)是一個Bundle對象,這個Bundle對象提供一系列的getXxx()和setXxx()方法用于傳遞基本數(shù)據(jù)類型的鍵值對,對于基本數(shù)據(jù)類型,使用起來很簡單,這里不再詳細講解。而對于復雜的數(shù)據(jù)類型,如一個對象的傳遞就要相對復雜一些。在Bundle中提供了兩個方法,專門用來傳遞對象的,但是這兩個方法也有相應的限制,需要實現(xiàn)特定的接口,當然,一些Android自帶的類,其實已經(jīng)實現(xiàn)了這兩個接口中的某一個,可以直接使用。方法如下:
putParcelable(String key,Parcelable value):需要傳遞的對象類實現(xiàn)Parcelable接口。
pubSerializable(String key,Serializable value):需要傳遞的對象類實現(xiàn)Serializable接口。
還有另外一種方式在Message中傳遞對象,那就是使用Message自帶的obj屬性傳值,它是一個Object類型,所以可以傳遞任意類型的對象,Message自帶的有如下幾個屬性:
int arg1:參數(shù)一,用于傳遞不復雜的數(shù)據(jù),復雜數(shù)據(jù)使用setData()傳遞。
int arg2:參數(shù)二,用于傳遞不復雜的數(shù)據(jù),復雜數(shù)據(jù)使用setData()傳遞。
Object obj:傳遞一個任意的對象。
int what:定義的消息碼,一般用于設定消息的標志。
注意
對于Message對象,一般并不推薦直接使用它的構造方法得到,而是建議通過使用Message.obtain()這個靜態(tài)的方法或者Handler.obtainMessage()獲取。Message.obtain()會從消息池中獲取一個Message對象,如果消息池中是空的,才會使用構造方法實例化一個新Message,這樣有利于消息資源的利用。并不需要擔心消息池中的消息過多,它是有上限的,上限為10個。Handler.obtainMessage()具有多個重載方法,如果查看源碼,會發(fā)現(xiàn)其實Handler.obtainMessage()在內(nèi)部也是調(diào)用的Message.obtain()。