該文章屬于Android Handler系列文章,如果想了解更多蝎宇,請點擊
《Android Handler機制之總目錄》
前言
上篇文章弟劲,我們講了ThreadLocal,了解了線程本地變量的實質(zhì)姥芥,如果有小伙伴還是不熟悉ThreadLocal原理的兔乞,請參看上篇文章《Android Handler機制之ThreadLocal》。如果你已經(jīng)閱讀 了該文章凉唐,那現(xiàn)在我們就一起來了解Handler與MessageQueue與Looper三者之間的關(guān)系及其內(nèi)部原理庸追。
Handler、MessageQueue台囱、Looper三者之間的關(guān)系
在了解其三者關(guān)系之前淡溯,我先給大家一個全局的關(guān)系圖,接下來的文章會根據(jù)該關(guān)系圖簿训,進行相應(yīng)的補充與描述咱娶。
從上圖中我們可以看出幾點
- Handler的創(chuàng)建是與Looper創(chuàng)建的線程是相同的米间。
- Looper中內(nèi)部維護了一個MessageQueue(也就是消息隊列)。且該隊列是通過鏈表的形式實現(xiàn)的膘侮。
- Hanlder最終通過sendMessage方法將消息發(fā)送到Looper中對應(yīng)的MessageQueue中屈糊。
- Looper通過消息循環(huán)獲取消息后,會調(diào)用對應(yīng)的消息中的target(target對應(yīng)的是發(fā)消息的Handler)的dispatchMessage()方法來處理消息琼了。
Looper原理
因為消息隊列(MessageQueue的創(chuàng)建是在Looper中內(nèi)部創(chuàng)建的逻锐,同時Handler消息的發(fā)送與處理都是圍繞著Looper來進行的,所以我們首先來講Looper雕薪。
Looper是如何與主線程關(guān)聯(lián)的
在平時開發(fā)中昧诱,我們使用Handler主要是為了在主線程中去更新UI,那么Looper是如何與主線程進行關(guān)聯(lián)的呢所袁?在Android中盏档,App進程是由Zygote fork 而創(chuàng)建的,而我們的ActivityThread就是運行在該進程下的主線程中纲熏,那么在ActivityThread的main方法中妆丘,Looper會通過prepareMainLooper()來創(chuàng)建內(nèi)部的消息隊列(MessageQueue),同時會通過loop()構(gòu)建消息循環(huán)锄俄。具體代碼如下圖所示:
public static void main(String[] args) {
...省略部分代碼
Looper.prepareMainLooper();//
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
要了解當前Loooper如何與主線程進行關(guān)聯(lián)的局劲,需要繼續(xù)查看prepareMainLooper()方法。下述代碼中奶赠,為了大家方便鱼填,我將prepareMainLooper()方法所涉及到的方法全部羅列了出來。
//創(chuàng)建主線程Looper對象
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
//Looper與當前主線程綁定
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));//創(chuàng)建Looper對象毅戈,放入主線程局部變量中
}
//獲取當前主線程的Looper對象
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
觀察上訴代碼苹丸,我們發(fā)現(xiàn),prepareMainLooper方法內(nèi)部調(diào)用prepare()方法(這里我們忽略該方法中的參數(shù) quitAllowed)苇经,而prepare內(nèi)部調(diào)用的是ThreadLocal的set()方法赘理。如果你閱讀了之前我寫的《Android Handler機制之ThreadLocal》。扇单,那么大家應(yīng)該知道了當前Looper對象已經(jīng)與主線程關(guān)聯(lián)了(也可以說商模,當前主線程中保存了當前Looper對象的引用)。
Looper內(nèi)部創(chuàng)建消息隊列
在了解了Looper對象怎么與當前線程關(guān)聯(lián)的后蜘澜,我們來看看Looper類中的具體方法施流。之前我們說過,在創(chuàng)建Looper對象的時候鄙信,當前Looper對象內(nèi)部也會創(chuàng)建與之關(guān)聯(lián)的消息隊列(MessageQueue)瞪醋。那么查看Looper對應(yīng)的構(gòu)造函數(shù):
final MessageQueue mQueue;
//Looper內(nèi)部會創(chuàng)建MessageQueue對象
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
從Looper對象的構(gòu)造函數(shù)中,我們很明顯的看出內(nèi)部創(chuàng)建了MessageQueue對象装诡,也驗證了我們之前的說法银受。
Looper的消息循環(huán)
當前Looper對象與主線程關(guān)聯(lián)后践盼,接著會調(diào)用Looper對象中的loop()方法來開啟消息循環(huán)。具體代碼如下圖所示:
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 (;;) {//一直循環(huán)去獲取消息隊列中的消息
Message msg = queue.next(); //該方法可能堵塞宾巍,
if (msg == null) {
//如果沒有消息宏侍,表示當前消息隊列已經(jīng)退出
return;
}
...省略部分代碼
try {
//獲取消息后,執(zhí)行發(fā)送消息的handler的dispatchMessage方法蜀漆。
msg.target.dispatchMessage(msg);
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
...省略部分代碼
}
msg.recycleUnchecked();
}
}
通過上述代碼谅河,我們可以看出,在Looper中的loop()方法中會一直去拿當前消息隊列中的消息确丢,如果能取出消息會調(diào)用該消息的target去執(zhí)行dispatchMessage()方法绷耍。如果沒有消息,就直接退出消息循環(huán)鲜侥。
MessageQueue原理
MessageQueue的next()方法
因為Looper中l(wèi)oop()方法會循環(huán)調(diào)用MessageQueue中的next方法褂始,接下來帶著大家一起查看該方法。代碼如下圖所示:
Message next() {
...省略部分代碼
for (;;) {
synchronized (this) {
...省略部分代碼
if (msg != null) {
if (now < msg.when) {
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
//遍歷消息列表描函,取出消息
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;
}
}
...省略部分代碼
}
}
}
上述代碼中崎苗,我省略了很多代碼,現(xiàn)在大家不需要關(guān)心省略的內(nèi)容舀寓,大家只要關(guān)心大的一個方向就夠了胆数,關(guān)于MessageQueue的next()具體詳解,會在下篇文章 《Android Handler機制之Message的發(fā)送與取出》具體介紹互墓。好了必尼,大家把狀態(tài)調(diào)整過來。
在上文中篡撵,我們說過MessageQueue是以鏈表的形式來存儲消息的判莉,從next()方法中我們能分析出來,next()方法會一直從MessageQueue中去獲取消息育谬,直到獲取消息后才會退出券盅。
MessageQueue的enqueueMessage()方法
通過上文,我們已經(jīng)了解Message取消息的流程膛檀,現(xiàn)在我們來看看消息隊列的加入過程锰镀。
boolean enqueueMessage(Message msg, long when) {
...省略部分代碼
synchronized (this) {
...省略部分代碼
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 {
...省略部分代碼
//循環(huán)遍歷消息隊列,把當前進入的消息放入合適的位置(比較等待時間)
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
//將消息插入合適的位置
msg.next = p;
prev.next = msg;
}
...省略部分代碼
}
return true;
}
上訴代碼中宿刮,我們把重心放在for循環(huán)中互站,在for循環(huán)中主要干了 一件事,就是根據(jù)當前meesag.when的值僵缺,來確定當前插入的消息應(yīng)該放入消息隊列的位置胡桃。(當前小伙伴肯能會對message.when感到困惑,還是那句話磕潮,現(xiàn)階段我們只用關(guān)心主要的流程翠胰,具體的方法詳解會在下篇文章 《Android Handler機制之Message的發(fā)送與取出》具體介紹)
Handler的原理
了解了Looper與MessageQueue的原理后容贝,我們大致了解了整個消息處理的關(guān)系,現(xiàn)在就剩下發(fā)消息與處理消息的流程了之景。最后一點了斤富,大家堅持看完。
Handler是怎么與Looper進行關(guān)聯(lián)的
在文章最開始的圖中锻狗,Handler發(fā)消息最終會發(fā)送到對應(yīng)的Looper下的MessageQueue中满力。那么也就是說Handler與Looper之間必然有關(guān)聯(lián)。那么它是怎么與Looper進行關(guān)聯(lián)的呢轻纪?查看Handler的構(gòu)造函數(shù):
//不帶Looper的構(gòu)造函數(shù)
public Handler() {this(null, false);}
public Handler(boolean async) {this(null, async);}
public Handler(Callback callback) {this(callback, false);}
public Handler(boolean async) {this(null, async);}
public Handler(Callback callback, boolean async) {
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.myLooper()內(nèi)部會調(diào)用sThreadLocal.get()油额,獲取線程中保存的looper局部變量
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
//獲取當前Looper中的MessageQueue
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
//帶Looper參數(shù)的構(gòu)造函數(shù)
public Handler(Looper looper) { this(looper, null, false); }
public Handler(Looper looper, Callback callback) { this(looper, callback, false);}
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
//獲取當前Looper中的MessageQueue
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
在Handler的構(gòu)造函數(shù)中,主要分為兩種類型的構(gòu)造函數(shù)刻帚,一種是帶Looper參數(shù)的構(gòu)造函數(shù)潦嘶,一種是不帶Looper參數(shù)的構(gòu)造函數(shù)。
- 在不帶Looper參數(shù)的構(gòu)造函數(shù)中崇众,是通過Looper.myLooper()來獲取當前Looper對象的(也就是說掂僵,Handler獲取的Looper對象是與當前實例化當前Handler的線程相關(guān)的,那么如果Handler對象是在主線程中創(chuàng)建的顷歌,那么獲取的就是主線程的Looper锰蓬,注意前提條件當前線程線程已經(jīng)通過Looper.prepare()與Looper.loop()構(gòu)建了循環(huán)消息隊列,因為只有調(diào)用了該方法后衙吩,才會將當前Looper對象放入線程的局部變量中)
- 在帶Looper參數(shù)的構(gòu)造函數(shù)中互妓,Looper對象是通過外部直接傳入的。(這里其實給我們提供了一個思路坤塞,也就是我們可以構(gòu)建自己的消息處理循環(huán),具體細節(jié)參看類HandlerThread)
Handler怎么將消息發(fā)送到MessaageQueue(消息隊列)中去
在了解Handler怎么將消息發(fā)送到MessageQueue(消息隊列)澈蚌,我們先來了解Handler的發(fā)消息的系列方法摹芙。
//發(fā)送及時消息
public final boolean sendMessage(Message msg)
public final boolean sendEmptyMessage(int what)
public final boolean post(Runnable r)
//發(fā)送延時消息
public final boolean sendEmptyMessageDelayed(int what, long delayMillis)
public final boolean sendMessageDelayed(Message msg, long delayMillis)
public final boolean postDelayed(Runnable r, long delayMillis)
//發(fā)送定時消息
public boolean sendMessageAtTime(Message msg, long uptimeMillis)
public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis)
public final boolean postAtTime(Runnable r, long uptimeMillis)
在Handler發(fā)消息的方法中,我們可以總共發(fā)消息的種類宛瞄,分為三種情況浮禾,第一種是及時消息,第二種是發(fā)送延時消息份汗,第三種是定時消息盈电。其中關(guān)于消息怎么在消息隊列中排列與處理。具體的方法詳解會在下篇文章《Android Handler機制之Message的發(fā)送與取出》具體介紹杯活。
通過查看Handler發(fā)送消息的幾個方法匆帚。我們發(fā)現(xiàn)內(nèi)部都調(diào)用了MessageQueue的enqueueMessage()方法。
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;//設(shè)置message.target為當前Handler對象
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);//獲取當前MessageQueue.將消息加入隊列中
}
該方法內(nèi)部其實很簡單旁钧,就是獲取當前MessageQueue對象吸重,將消息將入消息隊列中去了互拾。其中需要大家需要的注意的是這段代碼msg.target = this。該代碼意思就是當前的消息保存著當前發(fā)送消息的Handler對象的應(yīng)用嚎幸。該行代碼非常重要颜矿。因為最后涉及到消息的處理。
Handler怎么處理消息
通過上文的描述嫉晶,現(xiàn)在我們已經(jīng)大致了解Handler是怎么將消息加入到消息隊列中去了骑疆,現(xiàn)在需要關(guān)心的是當前消息是怎么被處理的。大家還記的之前我們講過的Looper原理吧替废,Looper會調(diào)用loop()方法循環(huán)的取消息封断。當取出消息后會調(diào)用message.target.dispatchMessage(Message msg)方法。其中message.target從上文我們已經(jīng)知道了舶担,就是當前發(fā)送消息的Handler坡疼。那么最終也就會回到Handler中的dispatchMessage()方法。
public void dispatchMessage(Message msg) {
if (msg.callback != null) {//第一步衣陶,判斷msg.callback
handleCallback(msg);
} else {
if (mCallback != null) {//第二步柄瑰、判斷Handler的callBack
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);//第三步,執(zhí)行Handler的handleMessage方法
}
}
觀察該方法剪况,我們可以得出教沾,Handler中的dispatchMessage()方法主要處理了三個步驟,下面分別對這三個步驟進行講解
第一步译断,執(zhí)行message.callback
在Handler中的dispatchMessage()方法中授翻,我們已經(jīng)知道如果msg.callback != null,那么我們會直接走handleCallback(msg)方法。在了解該方法之前孙咪,首先我們要知道m(xù)sg.callback對于的類是什么堪唐。這里我就直接給大家列出來了。其實msg.callback對應(yīng)是以下四個方法的Runnable對象翎蹈。
public final boolean post(Runnable r)
public final boolean postAtTime(Runnable r, long uptimeMillis)
public final boolean postAtTime(Runnable r, Object token, long uptimeMillis)
public final boolean postDelayed(Runnable r, long delayMillis)
以上四個方法在發(fā)送Runnable對象時淮菠,都會調(diào)用getPostMessage(Runnable r) 方法,且該方法都會將Runnable封裝在Message對象的callback屬性上荤堪。具體如下getPostMessage(Runnable r) 方法所示:
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
在了解了Message的callback到底什么過后合陵,我們再來看看handleCallback(Message message)方法
private static void handleCallback(Message message) {
message.callback.run();
}
該方法其實很簡單,就是調(diào)用相應(yīng)Runnable的run()方法澄阳。
第二步拥知,執(zhí)行Handler的callBack
如果當前Message.callback為空,接下來會判斷Handler中的Callback回調(diào)是否為空碎赢,如果不為空則執(zhí)行Callback的handleMessage(Message msg)方法低剔。Callback的具體聲明如下:
//避免創(chuàng)建Handler對象重新HandlerMessage方法,你可以直接傳入Callback接口實現(xiàn)
public interface Callback {
public boolean handleMessage(Message msg);
}
其中在Handler的幾個構(gòu)造函數(shù)中揩抡,可以傳入相應(yīng)Callback接口實現(xiàn)户侥。
public Handler(Callback callback)
public Handler(Looper looper, Callback callback)
public Handler(Callback callback, boolean async)
public Handler(Looper looper, Callback callback, boolean async)
第三步镀琉,執(zhí)行Handler的handleMessage)
如果都不滿足上面描述的第一、第二情況時蕊唐,會最終調(diào)用Handler的handleMessage(Message msg)方法屋摔。
//Handler內(nèi)部該方法是空實現(xiàn),需要子類具體實現(xiàn)
public void handleMessage(Message msg) { }
為了方便大家記憶替梨,我將Handler中的dispatchMessage()具體的邏輯流程畫了出來钓试。大家按需觀看。
最后
看到最后大家已經(jīng)發(fā)現(xiàn)該篇文章主要著重于將Handler機制的整個流程副瀑,對于很多的代碼細節(jié)并沒有過多的描述弓熏,特別是關(guān)于Looper從MessageQueue(消息隊列)中取消息與MessageQueue(消息隊列)怎么放入消息的具體細節(jié)。不用擔心糠睡,關(guān)于這兩個知識點將會在下篇文章《Android Handler機制之Message的發(fā)送與取出》具體描述挽鞠。