消息機(jī)制
主線程和子線程通信
消息機(jī)制涉及到三個(gè)角色,Handler、MessageQueue、Looper
基本實(shí)現(xiàn)
這里只介紹主線程handler創(chuàng)建方式,子線程后續(xù)源碼部分在介紹
- 創(chuàng)建Handler,重寫(xiě)handleMessage方法
- Handler發(fā)送消息
- handleMessage接收消息并處理
基本原理
Handler發(fā)送消息并最終添加到MessageQueue,Looper調(diào)用loop方法后開(kāi)始輪詢從MessageQueue中獲取消息并調(diào)用對(duì)應(yīng)的handler的dispatchMessage方法。
疑問(wèn)
- Handler如何發(fā)送消息的
- Looper是如何初始化,如何獲取消息的
- 消息機(jī)制如何實(shí)現(xiàn)線程間如何切換的
- Looper死循環(huán)為什么不會(huì)導(dǎo)致anr
- Linux的epoll機(jī)制
帶著疑問(wèn)我們通過(guò)下面源碼部分去解惑
源碼解析
- Handler到底是如何發(fā)送消息的
首先來(lái)看Hander創(chuàng)建
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()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
創(chuàng)建時(shí)會(huì)調(diào)用Looper.myLooper()獲取當(dāng)前線程的Looper對(duì)象
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
這里用到了ThreadLocal線程局部變量,內(nèi)部維護(hù)著一個(gè)ThreadLocalMap以map形式存儲(chǔ)局部變量,key是當(dāng)前線程value是局部變量。這樣就實(shí)現(xiàn)了線程之間數(shù)據(jù)隔離。避免數(shù)據(jù)沖突。
創(chuàng)建完Handler以后繼續(xù)看發(fā)送消息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);
}
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);
}
sendMessage會(huì)調(diào)用sendMessageDelayed緊接著又調(diào)用SendMessageAtTime,最終會(huì)調(diào)用enqueueMessage,將消息添加到消息隊(duì)列。這里消息隊(duì)列是哪里來(lái)的呢,在我們最開(kāi)始Handler初始化時(shí)有一行代碼mQueue = mLooper.mQueue;原來(lái)是來(lái)自Looper的MessageQueue,具體它的創(chuàng)建我們?cè)诤竺鍸ooper初始化在詳解。
接下來(lái)在看enqueueMessage如何將消息添加到消息隊(duì)列
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
Handler的enqueueMessage做了兩件事情,第一是將當(dāng)前handler引用賦值給message的target建立綁定關(guān)系以便于后面消息分發(fā)處理的時(shí)候知道要處理的消息屬于哪個(gè)Handler。第二件事情就是調(diào)用MessageQueue的enqueueMessage
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;
}
MessageQueue的enqueueMessage中會(huì)判斷當(dāng)前message是否需要立即執(zhí)行如果需要就添加到表頭,否則就根據(jù)時(shí)間輪詢比對(duì)添加到中間位置。
到這里Handler創(chuàng)建和消息發(fā)送以及添加到消息隊(duì)列過(guò)程就很清晰了,接下來(lái)要看Looper的創(chuàng)建和消息如何輪詢分發(fā)了。
- Looper是如何初始化,如何獲取消息的
這里又要回到Handler的初始化,在Handler初始化的時(shí)候回獲取當(dāng)前線程的Looper對(duì)象
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()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
這里可以看到如果獲取到的Looper對(duì)象為空就會(huì)拋出異常,所以Handler初始化之前必須要先初始化Looper,但是我們平時(shí)主線程并沒(méi)有手動(dòng)創(chuàng)建為什么不會(huì)報(bào)錯(cuò)呢?這是因?yàn)樵赼pp啟動(dòng)入口ActivityThread的main方法中系統(tǒng)已經(jīng)為我們做好了主線程的Looper初始化
public static void main(String[] args) {
//...忽略部分代碼
Looper.prepareMainLooper();
//...忽略部分代碼
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
那Looper到底是如何初始化的呢,接著看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();
}
}
prepareMainLooper又調(diào)用了prepare方法
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));
}
原來(lái)Looper的初始化是調(diào)用prepare方法,這里會(huì)創(chuàng)建looper對(duì)象并添加到ThreadLocal中,所以一個(gè)線程只能有一個(gè)Looper,并且prepare只能調(diào)用一次否則會(huì)報(bào)錯(cuò),
那Looper是如何獲取消息進(jìn)行分發(fā)的呢,在上面ActivityThread代碼中我們也可以看到Looper初始化完以后會(huì)調(diào)用Looper.loop()真相就在這里
public static void loop() {
final Looper me = myLooper();
//... 忽略部分代碼
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
//...忽略部分代碼
final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
final long dispatchEnd;
try {
msg.target.dispatchMessage(msg);
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
//...忽略部分代碼
msg.recycleUnchecked();
}
}
我們可以看到loop方法輪詢從MessageQueue中取出消息,判斷如果需要立即執(zhí)行就會(huì)調(diào)用meesage的target的dispatchtMessage方法進(jìn)行消息分發(fā)處理。message的target就是上面提到的message所屬的handler,因此會(huì)回調(diào)到Handler的dispatchMessage方法
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
dispatchMessage會(huì)判斷message的callback是否有設(shè)置,查看源碼可以知道setCallBack是hide方法只能通過(guò)反射調(diào)用它,如果它為空繼續(xù)判斷mCallBack是否為空,mCallBack哪兒來(lái)的呢,在Handler初始化時(shí)候有一個(gè)參數(shù)就是是否設(shè)置callback
public Handler(Callback callback, boolean async) {
//...忽略部分代碼
mCallback = callback;
//...忽略部分代碼
}
如果mCallBack有設(shè)置會(huì)直接調(diào)用CallBack的handleMessge方法否則繼續(xù)調(diào)用handler的handlemessage方法,因?yàn)槲覀冊(cè)诔跏蓟疕andler的時(shí)候回重寫(xiě)handlemessage方法所以最終會(huì)回調(diào)到我們重寫(xiě)的handlemessage方法里。
消息機(jī)制是如何實(shí)現(xiàn)線程間切換的?
我們通常會(huì)使用handler在子線程發(fā)送消息,然后回調(diào)到主線程處理消息。那到底是如何實(shí)現(xiàn)線程切換的呢,我們通過(guò)前面的分析可以知道,一個(gè)完整的消息分發(fā)流程,應(yīng)該是先調(diào)用Looper.prepare()初始化Looper和消息隊(duì)列,然后調(diào)用Looper.loop()開(kāi)啟輪詢。然后創(chuàng)建Handler在指定線程發(fā)送消息接受回調(diào)處理消息。那回調(diào)是在哪個(gè)線程呢,重點(diǎn)來(lái)了:因?yàn)橄⒎职l(fā)是Looper通過(guò)輪詢拿到Message的target也就是發(fā)送者Handler在調(diào)用Handler的dispatchMessage方法完成分發(fā)的,這個(gè)調(diào)用是在Looper中,所以調(diào)用者在哪個(gè)線程當(dāng)前回調(diào)就是在哪個(gè)線程,所以Looper初始化時(shí)綁定的線程就是回調(diào)的線程。Looper死循環(huán)為什么不會(huì)導(dǎo)致anr
App本質(zhì)上也是一個(gè)java程序,入口就是ActivityThread的main方法,如果main方法執(zhí)行完程序就推出了,如何保證不退出就是寫(xiě)一個(gè)死循環(huán),ActivityThread中初始化了Looper并調(diào)用了loop在loop方法中開(kāi)啟了一個(gè)死循環(huán)阻塞了主線程這樣程序可以保證程序一直執(zhí)行不會(huì)退出。幾乎所有的GUI程序都是這么實(shí)現(xiàn)的。既然是死循環(huán)那么其他代碼怎么運(yùn)行,頁(yè)面交互怎么處理呢?Android是基于事件驅(qū)動(dòng)的,不管是頁(yè)面刷新還是交互本質(zhì)上都是事件,都會(huì)被封裝成Message發(fā)送到MessageQueue由Looper進(jìn)行分發(fā)處理的。ANR是什么,Application no responding應(yīng)用無(wú)響應(yīng),為什么沒(méi)響應(yīng),因?yàn)橹骶€程做了好事操作,loop方法死循環(huán)也會(huì)阻塞主線程為什么不會(huì)anr,什么是響應(yīng),響應(yīng)就是頁(yè)面刷新,交互處理等,誰(shuí)來(lái)響應(yīng),其實(shí)就是looper的loop方法,,主線程做了耗時(shí)操作會(huì)阻塞loop方法導(dǎo)致無(wú)法處理其他message所以導(dǎo)致anr。Linux的epoll機(jī)制
我們先來(lái)看下Message的next方法
Message next() {
//...省略部分代碼
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
//重點(diǎn)關(guān)注這一行
nativePollOnce(ptr, nextPollTimeoutMillis);
//...省略部分代碼
}
}
在MessageQueue的next方法中會(huì)調(diào)用本地方法nativePollOnce,它是以阻塞的方式從native層的MessageQueue中獲取可用消息,也就是會(huì)進(jìn)行休眠。當(dāng)有可用消息時(shí)進(jìn)行喚醒。Looper的休眠和喚醒的實(shí)現(xiàn)原理是Linux的epoll機(jī)制,Looper的休眠和喚醒是通過(guò)對(duì)文件可讀事件監(jiān)聽(tīng)來(lái)實(shí)現(xiàn)喚醒。
什么是Linux的epoll機(jī)制?
Linux的epoll機(jī)制可以簡(jiǎn)單理解為事件監(jiān)聽(tīng),當(dāng)空閑的時(shí)候讓出cpu進(jìn)行休眠,當(dāng)事件觸發(fā)的時(shí)候會(huì)被喚醒開(kāi)始處理任務(wù)。