談起Android 消息機(jī)制,相信各位會首先想到Handler,Handler是Android 提供給給開發(fā)者實現(xiàn)線程間通信的工具。Android的消息機(jī)制包含四大內(nèi)容,ThreadLocal保證每個線程都有自己的消息輪詢器Looper,MessageQueue用來存放消息,Looper負(fù)責(zé)取消息,最后Handler負(fù)責(zé)消息的發(fā)送與消息的處理。
- 先來一張腦圖回顧整體知識

ThreadLocal
- 我們知道,每個Handler 都有其所在線程對應(yīng)的Looper,查看Handler構(gòu)造方法
/**Handler 構(gòu)造方法*/
public Handler(Callback callback, boolean async) {
.......
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
......
}
/** Looper 中 sThreadLocal 聲明*/
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
/** Looper 中 myLooper方法*/
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
/** Looper 中 prepare方法*/
private static void prepare(boolean quitAllowed) {
....
sThreadLocal.set(new Looper(quitAllowed));
}
- 通過以上源碼,可以知道,Looper.myLooper()獲取不到Looper則會拋異常,所以創(chuàng)建Handler之前都要調(diào)用一下Looper.prepare方法,也就是在該方法中新建了Looper并存放到ThreadLocal中。這里就會產(chǎn)生一個疑問,ThreadLocal能保證每個線程有自己對應(yīng)的Looper?沒錯,它就真能保證,接下來就看看什么是ThreadLocal。
什么是ThreadLocal
ThreadLocal是一個線程內(nèi)部數(shù)據(jù)存儲類,但存放數(shù)據(jù)并不是它實現(xiàn)的,它只是幫助類,真正存放數(shù)據(jù)的是ThreadLocalMap。
先看一個簡單的例子
public class Test {
static ThreadLocal<String> name =new ThreadLocal<>();
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
name.set("xiaoming");
System.out.println("---------------"+name.get()+"-------------------");
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("---------------"+name.get()+"-------------------");
}
}).start();
}
}
### 運行結(jié)果
> Task :Test.main()
---------------xiaoming-------------------
---------------null-------------------
- 上面例子當(dāng)中,兩個線程訪問的都是一個ThreadLocal對象,但是第二個線程沒有設(shè)置初始值,則獲取為null,也就可以說明每個線程操作的是自己對應(yīng)的一份數(shù)據(jù),雖然都是從ThreadLocal的get方法獲取,但是get方法則是獲取對應(yīng)線程的ThreadLocal.ThreadLocalMap來獲取值。
ThreadLocal分析
ThreadLocal的set方法
/**
* Sets the current thread's copy of this thread-local variable
* to the specified value. Most subclasses will have no need to
* override this method, relying solely on the {@link #initialValue}
* method to set the values of thread-locals.
*
* @param value the value to be stored in the current thread's copy of
* this thread-local.
*/
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
/**
* Get the map associated with a ThreadLocal. Overridden in
* InheritableThreadLocal.
*
* @param t the current thread
* @return the map
*/
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
/**
* Create the map associated with a ThreadLocal. Overridden in
* InheritableThreadLocal.
*
* @param t the current thread
* @param firstValue value for the initial entry of the map
*/
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
- 通過以上代碼,代碼層面首先獲取當(dāng)前線程,然后獲取
ThreadLocalMap,如果存在,則獲取當(dāng)前線程的ThreadLocalMap;如果不存在則根據(jù)當(dāng)前線程和當(dāng)前需要存入的數(shù)據(jù)新建ThreadLocalMap來存放線程內(nèi)部數(shù)據(jù),也就是當(dāng)前ThreadLocal作為key,而存儲的值最為value來存儲。
ThreadLocal的get方法
/**
* Returns the value in the current thread's copy of this
* thread-local variable. If the variable has no value for the
* current thread, it is first initialized to the value returned
* by an invocation of the {@link #initialValue} method.
*
* @return the current thread's value of this thread-local
*/
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
/**
* Variant of set() to establish initialValue. Used instead
* of set() in case user has overridden the set() method.
*
* @return the initial value
*/
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
protected T initialValue() {
return null;
}
- 從以上代碼,ThreadLocal的get方法根據(jù)當(dāng)前線程來獲取對應(yīng)的ThreadLocalMap,如果獲取不到,說明還沒有創(chuàng)建,由createMap方法來創(chuàng)建ThreadLocalMap,initialValue方法則設(shè)置了value的初始值為null,也呼應(yīng)前面的例子打印結(jié)果。
ThreadLocal原理
Thread類有一個類型為ThreadLocal.ThreadLocalMap的成員變量threadLocals,如果你了解Java內(nèi)存模型,threadLocals的值都是new出來的話,很容易明白threadLocals是存放在堆內(nèi)存中的,而每一個線程只是在堆內(nèi)存中存放了自己的threadLocals,也就是每個線程本地內(nèi)存(邏輯上),物理上本地內(nèi)存只是在堆內(nèi)存中占有一塊區(qū)域,每個線程只玩自己對應(yīng)的threadLocals,各個線程的對應(yīng)ThreadLocal互不干擾,這也就實現(xiàn)了各個線程間數(shù)據(jù)的隔離,也就是每個Handler所在線程都有其對應(yīng)的Looper對象。
Thread類中 threadLocals 聲明
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
簡單來說就是數(shù)據(jù)復(fù)制很多份存放在堆內(nèi)存,各個線程獲取自己對應(yīng)的那份數(shù)據(jù)。
這個可以舉一個共享汽車的例子,假如剛開始共享汽車試運行,大街上只有一輛,大家都搶著去開,這就會出現(xiàn)問題,而后來發(fā)展普及,每輛車復(fù)制迅速生產(chǎn),滿大街都是共享汽車,每個人都可以通過專屬二維碼開對應(yīng)共享汽車,這里開車人就對應(yīng)線程,大家互不干擾,共享汽車就對應(yīng)ThreadLocals,而大街就相當(dāng)于堆內(nèi)存。

ThreadLocalMap
- ThreadLocal中真正存放數(shù)據(jù)的是ThreadLocalMap,他的內(nèi)部實現(xiàn)是一個環(huán)形數(shù)組來存放數(shù)據(jù),具體分析可以查看以下文章,這里就不在進(jìn)行展開了。
- ThreadLocal源碼解讀
MessageQueue消息隊列工作原理
- MessageQueue字面意思是消息隊列,而他的實現(xiàn)則不是消息隊列,它的內(nèi)部實現(xiàn)數(shù)據(jù)結(jié)構(gòu)為單鏈表,單鏈表在頻繁插入刪除方面是有優(yōu)勢的,鏈表的插入刪除操作對應(yīng)消息的存儲和取出,方法分別對應(yīng)enqueueMessage和next方法。
存放消息enqueueMessage
- 查看Handler的源碼,很容易發(fā)現(xiàn)發(fā)消息的方法最終都是調(diào)用了sendMessageAtTime方法,uptimeMillis為系統(tǒng)開機(jī)時間加上設(shè)置消息的延時時間,Handler的enqueueMessage方法將Message的Target設(shè)為當(dāng)前Handler,存放消息則調(diào)用了MessageQueue的enqueueMessage方法。
/** Handler的sendMessageAtTime方法*/
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
......
return enqueueMessage(queue, msg, uptimeMillis);
}
/** Handler的enqueueMessage方法*/
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
.......
return queue.enqueueMessage(msg, uptimeMillis);
}
- 接著存放消息看到MessageQueue的enqueueMessage方法
/** MessageQueue的enqueueMessage方法*/
boolean enqueueMessage(Message msg, long when) {
......
synchronized (this) {
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
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;
}
- 通過以上源碼,enqueueMessage邏輯主要為單鏈表的插入操作,如果鏈表中沒有消息,或者當(dāng)前存入消息延時為零,又或者當(dāng)前存入消息延時小于鏈表P節(jié)點的延時,則將當(dāng)前消息插入到鏈表的頭節(jié)點,否則遍歷鏈表中的每個節(jié)點,找延時小于當(dāng)前消息的節(jié)點存入消息。話句話說,單鏈表里面消息是按Message的觸發(fā)時間順序排序的。
取消息 next
- 接著看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;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
//省略部分代碼
}
// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
nextPollTimeoutMillis = 0;
}
}
- 通過以上代碼,nextPollTimeoutMillis字段是關(guān)鍵,它代表next在獲取下一個消息時需要等待的時長,他的取值有三種情況:
- 當(dāng)nextPollTimeoutMillis小于零,表示消息隊列中無消息,會一直等待下去
- 當(dāng)nextPollTimeoutMillis等于零,則不會等待,直接出了取出消息
- 當(dāng)nextPollTimeoutMillis大于零,則等待nextPollTimeoutMillis值的時間,單位是毫秒
- 通過對nextPollTimeoutMillis的了解,next方法是如何等待呢?換個詞可能更準(zhǔn)確,應(yīng)該叫阻塞,這里注意到next方法循環(huán)中的nativePollOnce(ptr, nextPollTimeoutMillis)方法,它的實現(xiàn)在native層,可以實現(xiàn)阻塞的功能,具體原理是使用epoll,它是一種linux的I/O事件通知機(jī)制,I/O輸入輸出對象使用的是管道(pipe),具體native層分析請看Gityuan大佬的分析文章Android消息機(jī)制2-Handler(Native層)
private native static void nativeWake(long ptr);
private native void nativePollOnce(long ptr, int timeoutMillis); /*non-static for callbacks*/
到此,next方法的邏輯就很清晰了,開始nextPollTimeoutMillis的值是等于零的,獲取消息過程就不會受到nativePollOnce方法的阻塞,然后判斷取出的消息是否延時,有延時則計算nextPollTimeoutMillis進(jìn)入下一循環(huán)進(jìn)入nativePollOnce方法阻塞,否則返回取出的消息,有阻塞肯定就有喚醒,這個喚醒的方法就是nativeWake(long ptr)方法,它的實現(xiàn)也在native層,它的調(diào)用在我們前面分析enqueueMessage方法邏輯有出現(xiàn),當(dāng)有消息進(jìn)入消息隊列,如果當(dāng)前線程正在被阻塞,調(diào)用nativeWake方法,nativePollOnce就會立即返回,取消阻塞,這樣循環(huán)取到?jīng)]有延時的消息,則直接返回消息;如果沒有消息,nextPollTimeoutMillis等于 -1,繼續(xù)阻塞狀態(tài)。
經(jīng)過前面的分析,消息插入鏈表是sendMessageAtTime方法觸發(fā)的,而接下來就會有一個疑問,那又是誰調(diào)用 next() 方法取消息呢?沒錯,就是接下來要了解的Looper
Looper 工作原理
- Looper在Android消息機(jī)制中是消息輪詢器的作用,他會不斷到MessageQueue中去取消息,取消息根據(jù)前面next 方法分析,如果阻塞,則說明沒有消息
- 先看Looper源碼注釋中有一段示例代碼
/* This is a typical example of the implementation of a Looper thread,
* using the separation of {@link #prepare} and {@link #loop} to create an
* initial Handler to communicate with the Looper. */
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();
}
}
- 由example代碼所示,使用 Handler 之前調(diào)用了Looper.prepare(),如下代碼所示,就是在ThreadLocal中存放當(dāng)前線程的Looper對象,在Looper構(gòu)造方法中創(chuàng)建了MessageQueue
public static void prepare() {
prepare(true);
}
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));
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
- 接著創(chuàng)建完Handler之后,又調(diào)用Looper.loop()方法,如下
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the 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
//省略部分代碼.....
try {
msg.target.dispatchMessage(msg);
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} finally {
//省略部分代碼。。。。
}
//省略部分代碼。。。。
}
}
- 首先看到第一行myLooper(),前面在分析ThreadLocal已經(jīng)了解過,myLooper就是獲取ThreadLocal獲取我們存儲的Looper對象,如果獲取不到就會報異常,提示我們我們沒有調(diào)用Looper.prepare(),這也就是子線程使用Handler必須調(diào)用Looper.prepare()的原因。是不是有恍然大悟的感覺。然后就是就是根據(jù)構(gòu)造方法創(chuàng)建的MessageQueue來獲取消息queue.next(),該方法經(jīng)過前面分析在沒有消息或者消息延時時間還沒到是阻塞的;獲取到消息后,根據(jù)msg.target.dispatchMessage(msg)調(diào)用的便是Handler的dispatchMessage方法(前文分析中msg.target的值為當(dāng)前Handler)。
主線程Looper.prepare()
- 經(jīng)過前面的分析,你也許會有一個疑問,在Android使用Handler怎么不用調(diào)用Looper.prepare()方法?
- 解下來我們看到Android的主線程ActivityThread的main方法,嚴(yán)格來說,ActivityThread并不是線程類,但是Android主線程肯定是存在的,只是主線程在ActivityThread的 main 方法中創(chuàng)建,并在該方法調(diào)用了Looper.prepareMainLooper() 方法和Looper.loop() 方法,所以我們在Android 主線程就可以直接使用Handler
/**ActivityThread 的 main 方法*/
public static void main(String[] args) {
//省略部分代碼....
Looper.prepareMainLooper();
//省略部分代碼....
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
//省略部分代碼....
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();
}
//省略部分代碼....
}
/**Looper 的 prepareMainLooper 方法*/
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
Handler 工作原理
- 前面已經(jīng)了解過Handler發(fā)送消息的sendMessageAtTime方法,接著我們來看看Handler的dispatchMessage方法
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
private static void handleCallback(Message message) {
message.callback.run();
}
- 這里邏輯就很簡單了,如果發(fā)送的消息設(shè)置了Runnable類型的callback對象,則調(diào)用他的run方法,沒有則判斷是否設(shè)置了Handler.Callback,設(shè)置則調(diào)用Handler.Callback接口的handleMessage方法,否則調(diào)用Handler空實現(xiàn)方法handleMessage。

Looper.loop()死循環(huán),為什么不會導(dǎo)致主線程發(fā)生ANR?
- 根據(jù)前面的分析,Looper.loop()的方法獲取不到數(shù)據(jù),則會阻塞,這個阻塞和卡死是兩回事,阻塞是Linux pipe/epoll機(jī)制文件讀寫的等待,等待及休眠,則會釋放占用CPU的資源,而我們開發(fā)遇見的卡死一般都是在主線程做了太多耗時操作,Activity 5s,BroadcastReceiver 10s和Service 20s未響應(yīng)引起的ANR,具體背后分析還請看Gityuan的知乎解答Android中為什么主線程不會因為Looper.loop()里的死循環(huán)卡死?
參考
書籍
- 《Android開發(fā)藝術(shù)探索》