Android中的Handler一般是用于異步任務(wù),和Handler相關(guān)的一些概念有Looper,MessageQueue,下面先通過一張圖來展示三者只之間的關(guān)系。

MessageQueue主要是維護(hù)消息隊(duì)列,Handler主要是消息的發(fā)送和處理,Looper扮演著管理者這么一個角色,由它來維護(hù)這個流程的正常執(zhí)行。
首先來分析一下這個管理者Looper,它是一個線程獨(dú)享的變量,所以再此之間還得先了解一個概念ThreadLocal,這是Android系統(tǒng)提供的一個類,用于實(shí)現(xiàn)線程獨(dú)享。
ThreadLocal的使用 。
//存儲 ThreadLocal<Integer> tl = new ThreadLocal<>(); Integer var = new Integer(1); tl.set(var); //使用。在哪個線程中調(diào)用,就會使用哪個線程中的備份。 Integer local = tl.get();-
ThreadLocal原理。
在使用ThreadLocal時(shí)主要會用到兩個方法,set()和get()方法。直接從源碼看起。
public void set(T value) {
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);
if (values == null) {
values = initializeValues(currentThread);
}
values.put(this, value);
}
set的代碼很簡單,首先獲得當(dāng)前線程,再獲得一個Values變量,最后是調(diào)用了values.put(this,value)方法。
public T get() {
// Optimized for the fast path.
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);
if (values != null) {
Object[] table = values.table;
int index = hash & values.mask;
if (this.reference == table[index]) {
return (T) table[index + 1];
}
} else {
values = initializeValues(currentThread);
}return (T) values.getAfterMiss(this); }
同樣get的邏輯也很清楚,首先獲得當(dāng)前線程,然后獲得一個Values變量,通過values中的table屬性來獲得所需的值。
set和get中都出現(xiàn)了一個類Value,接下來看一下Value這個類。Values是ThreadLocal的靜態(tài)內(nèi)部類,我們主要關(guān)注一下其中的table屬性,put和value方法。
Values values(Thread current) {
return current.localValues;
}
values方法非常簡單,就是獲得當(dāng)前線程的localValues變量。而localValues變量就是Thread類中的一個屬性。
Thread { ThreadLocal.Values localValues; } 想必看到這就明白為什么在不同線程環(huán)境下能夠獲得各自線程的本地變量了。因?yàn)閷?shí)質(zhì)上就是在每個Thread內(nèi)部存儲了一個本地變量,通過空間來換時(shí)間達(dá)到同步的作用。
static class Values {
/**
* Map entries. Contains alternating keys (ThreadLocal) and values.
* The length is always a power of 2.
*/
private Object[] table;
void put(ThreadLocal<?> key, Object value) {
cleanUp();
// Keep track of first tombstone. That's where we want to go back
// and add an entry if necessary.
int firstTombstone = -1;
for (int index = key.hash & mask;; index = next(index)) {
Object k = table[index];
if (k == key.reference) {
// Replace existing entry.
table[index + 1] = value;
return;
}
if (k == null) {
if (firstTombstone == -1) {
// Fill in null slot.
table[index] = key.reference;
table[index + 1] = value;
size++;
return;
}
// Go back and replace first tombstone.
table[firstTombstone] = key.reference;
table[firstTombstone + 1] = value;
tombstones--;
size++;
return;
}
// Remember first tombstone.
if (firstTombstone == -1 && k == TOMBSTONE) {
firstTombstone = index;
}
}
}
}
table變量很簡單,就是一個Object數(shù)組,用于存儲所需的值。接下來分析一下put方法。主體就是一個for循環(huán),遍歷所有的index,可以把index當(dāng)做映射關(guān)系中的鍵,通過對應(yīng)的鍵最終取到需要的值。
private static AtomicInteger hashCounter = new AtomicInteger(0); private final int hash = hashCounter.getAndAdd(0x61c88647 * 2); //Weak reference to this thread local instance. private final Reference<ThreadLocal<T>> reference = new WeakReference<ThreadLocal<T>>(this);
把table作為鍵值的存儲結(jié)構(gòu),index下標(biāo)所對應(yīng)的為鍵,index + 1下標(biāo)所對應(yīng)的為實(shí)際存儲的值。
如果k已經(jīng)存在,就直接更新它的值。如果k不存在,就把key.reference存入table中的index下標(biāo)位置,把值存在table中index + 1的下標(biāo)位置。
Looper-消息的管理者
在理解了ThreadLocal的原理之后,就可以明白Looper為什么是線程獨(dú)享的了。一般的使用方法如下:
Looper.prepare(); . . . Looper.loop();
handler必須得在這個代碼塊中間初始化。
首先看看prepare方法中的代碼:
public static void prepare() {
prepare(true);
}
就調(diào)用了一個重載方法:
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));
}
sThreadLoca就是Looper內(nèi)部的ThreadLocal屬性。首先通過get方法獲得Looper變量,如果不為空,就會報(bào)錯,也就是Looper.prepare方法為什么只能調(diào)用一次的原因。如果不為空,就調(diào)用set方法,把Looper變量存儲ThreadLocal的table中。
接下來分析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.");
}
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;
}
final long traceTag = me.mTraceTag;
try {
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
}
}
首先獲得Looper變量me,myLooper方法很簡單,如下:
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
就是通過sThreadLocal的get方法獲得當(dāng)前線程的Looper變量。
然判斷me是否為空,為空就報(bào)錯,也就是說loop方法必須在prepare方法之后調(diào)用。
接著拿到MessageQueue對象,這個對象是Looper的內(nèi)部屬性,在構(gòu)造函數(shù)中進(jìn)行初始化。
隨后就是for循環(huán),這是一個無限循環(huán)體,退出的唯一條件就是獲取到的消息為null。
通過queue去拿到需要處理的消息(這個消息是通過handler發(fā)出的),然后注意next方法是會阻塞的,也就是如果沒有消息,程序會一直停留在這一行代碼。
當(dāng)取得消息不為空,就執(zhí)行msg.target.dispatchMessage(msg),msg.target其實(shí)就是handler,handler中有一個方法dispatchMessage(Message msg),這樣就把消息的處理交到了handler的手里,完成了Looper的交接任務(wù)。-
MessageQueue-消息隊(duì)列
MessageQueue主要就是維持一個消息隊(duì)列。主要的方法就是enqueueMessage和next。
boolean enqueueMessage(Message msg, long when) {
synchronized (this) {
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}// We can assume mPtr != 0 because mQuitting is false. if (needWake) { nativeWake(mPtr); } } return true; }
mMessages是MessageQueue中的屬性,它指向消息隊(duì)列的頭部,即下一刻需要處理的消息。這里第一個if判斷需要添加新的頭部。條件1是當(dāng)前隊(duì)列為空,條件2是當(dāng)前加入的消息為立刻處理,條件3是當(dāng)前加入的消息比隊(duì)列首部的消息優(yōu)先級高(通過消息需要觸發(fā)時(shí)的時(shí)間先后來判斷)。三者任意一個滿足,就需要把當(dāng)前消息作為頭部加入消息隊(duì)列。
如果不滿足,則進(jìn)入else部分。通過一個for循環(huán),把消息插入到隊(duì)列的中間,具體的位置是根據(jù)優(yōu)先級來判斷。
這樣消息的插入就完成了。接下來分析如何取出消息(核心代碼):
Message next() {
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
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.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
}
}
方法的主體就是一個for無限循環(huán)體,這也就解釋了為什么在Looper中調(diào)用MessageQueue的next方法會阻塞了。now是系統(tǒng)當(dāng)前的時(shí)間,msg即當(dāng)前的頭部消息,如果不為空,且msg.when 大于 now,即說明當(dāng)前消息還沒有到需要處理的時(shí)候,那么讓線程掛起。等待nextPollTimeoutMillis 時(shí)間后,喚醒線程繼續(xù)處理消息。
如果消息已經(jīng)可以處理,那么移動頭部引用mMessage為msg的下一個,然后返回當(dāng)前消息。
至此,消息的添加和取出的邏輯已經(jīng)分析完畢。
-
Handler-消息的處理者
首先看handler的使用代碼:
public static Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
//具體邏輯處理
}
};
一般都是實(shí)現(xiàn)一個匿名內(nèi)部類,然后重寫handleMessage方法。下面分析一下,handler的構(gòu)造函數(shù)進(jìn)行了哪些操作:
public Handler(Callback callback, boolean async) {
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
Handler有很多重載的構(gòu)造函數(shù),但最后都會調(diào)用此方法。首先獲得當(dāng)前所關(guān)聯(lián)的Looper,這也就是為什么handler的初始化必須在Looper.prepare()之后了。隨后獲取當(dāng)前的消息隊(duì)列。
接下來分析一下dispatchMessage方法,想必大家還記得在Looper中取到消息后調(diào)用了msg.target,dispatchMessage(),這里就分析一下消息是怎么處理的:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
首先是判斷msg.callback,如果 不為空則直接調(diào)用handleCallback方法。而msg的callback其實(shí)就是一個Runnable對象,handleCallback方法也就是簡單的調(diào)用了Runnable的run方法。
private static void handleCallback(Message message) {
message.callback.run();
}
如果msg.callback為空,則判斷mCallback是否為空,而mCallback就是Handler的一個內(nèi)部接口:
public interface Callback {
public boolean handleMessage(Message msg);
}
這個接口為用戶提供了另外一種實(shí)現(xiàn)handler的方法,就是實(shí)現(xiàn)一個Callback,最為參數(shù)傳入Handler的構(gòu)造函數(shù),然后具體的消息處理邏輯就在Callback的handleMessage方法中去實(shí)現(xiàn),代碼實(shí)例如下:
public static Handler mHandler = new Handler(new Handler.Callback(){
@Override
public void handleMessage(Message msg) {
//具體邏輯處理
}
});
再回到dispatchMessage方法中,如果mCallback.handleMessage(msg)返回了true,那么消息處理就結(jié)束了,如果返回了false,或者mCallback為空,則就會調(diào)用Handler的handleMessage(msg)方法,該方法默認(rèn)是空方法,沒有任何的操作。
消息的處理流程理清楚后再來看看消息的發(fā)送,主要有post和sendMessage兩大類方法。
post方法主要是通過Runnable,設(shè)置為msg.callback。下邊來看一個post方法:
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
跟蹤一下sendMessageDelayed和getPostMessage方法:
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}public final boolean sendMessageDelayed(Message msg, long delayMillis) { if (delayMillis < 0) { delayMillis = 0; } return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); }
這兩個方法很簡單,最終會進(jìn)入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);
}
這個方法同樣邏輯非常簡單,那么再進(jìn)入enqueueMessage方法:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
這里終于出現(xiàn)了msg.target = this,原來handler是在發(fā)送消息的時(shí)候關(guān)聯(lián)到具體的msg上的。
最后調(diào)用了queue的enqueueMessage方法,這個方法已經(jīng)在上邊分析過了。
下邊來分析一下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);
}
同樣,最終還是會調(diào)用sendMessageAtTime這個方法。
- 注意事項(xiàng)
1 Handler在哪個線程進(jìn)行的初始化,那么最終消息的處理線程就一定是這個線程。
2 Handler一定要在Looper初始化以后才能進(jìn)行初始化。
3 主線程默認(rèn)已經(jīng)調(diào)用了Looper.loop(),所以才可以直接實(shí)例化Handler,若是在其他線程需要實(shí)例化Handler,那么一定要先調(diào)用Looper.loop(),為當(dāng)前線程初始化自己的Looper對象。