平時使用Handler的時候绅作,我們都知道調(diào)用了Handler.sendMessage()
方法后怔鳖,消息會在handleMessage()
中被處理,或者調(diào)用了Handler.post()
之后,Runnable
會被在一定的時機下得到執(zhí)行宽气。但至于什么時候,可能就不會去在意這些細節(jié)了潜沦。雖然很久前就看過了Handler的機制萄涯,但那時候是為了學(xué)習(xí)而學(xué)習(xí),一旦跟實際聯(lián)系起來就脫節(jié)了唆鸡。比如說:
- 都知道Android的
main
方法在ActivityThread類中涝影,并且在該方法里面調(diào)用了Looper.loop();
讓主線程進入了死循環(huán),但是我們平時執(zhí)行的代碼也是在主線程的争占,他們是怎么跳過這個死循環(huán)去執(zhí)行的燃逻? - 有時候需要在onCreate方法中做一些延時操作,我們就會利用
Handler.sendMessage()
或者Handler.post()
方法燃乍,這樣就能將相應(yīng)的邏輯延遲到onResume
之后了唆樊。但這又是如何保證它會延遲到onResume
之后的?
帶著這兩個問題刻蟹,重新來學(xué)習(xí)一下Handler的消息機制逗旁。
用一句話來總結(jié)Handler的消息機制就是:Looper通過死循環(huán)不斷地去查詢MessageQueue中有沒有新的消息,有的話取出來處理,處理之完繼續(xù)不斷的查詢....實際上片效,代碼也確實是這樣寫的红伦。
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
// 在設(shè)置了退出標(biāo)志的時候,這里取出的msg就會為null
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
......
try {
// 讓msg對應(yīng)的Handler去處理消息
msg.target.dispatchMessage(msg);
} finally {
...
}
}
}
需要注意的是淀衣,一個線程里面只能有一個Looper昙读,而一個Looper對應(yīng)著一個MessageQueue。所以在一個線程里面膨桥,無論你有多少個Handler蛮浑,最終發(fā)送的消息都要在同一個MessageQueue中排隊等待被執(zhí)行。
Handler無非就是往MessageQueue中發(fā)送消息而已只嚣,而MessageQueu的任務(wù)就是接收Handler的消息沮稚,并接受Looper的查詢而已(簡單點來理解就好了。)
到了這里册舞,勉強算是又重溫了一遍Handler的消息機制蕴掏,再來看一下第一個問題,先看看ActivityThread的main
方法:
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
SamplingProfilerIntegration.start();
// CloseGuard defaults to true and can be quite spammy. We
// disable it here, but selectively enable it later (via
// StrictMode) on debug builds, but using DropBox, not logs.
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
// Set the reporter for event logging in libcore
EventLogger.setReporter(new EventLoggingReporter());
// Make sure TrustedCertificateStore looks in the right place for CA certificates
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir);
Process.setArgV0("<pre-initialized>");
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");
}
Looper.loop()
方法之后是一個異常调鲸,也就是說盛杰,如果我們的代碼跳出了Looper的循環(huán),程序也就掛了藐石,而這個循環(huán)又和我們的onCreate
一樣是在主線程中執(zhí)行的即供,那么就剩下一種可能了,我們平時執(zhí)行的代碼就是在Looper.loop()
中去執(zhí)行的贯钩,loop
方法我們也看了募狂,就是從消息隊列中取出消息然后去處理消息。也就是說角雷,其它線程通過主線程的Handler來發(fā)送Message祸穷,從而讓Message在主線程中去處理的。從main
方法可以看出消息不可能是在主線程中發(fā)出的勺三。為了驗證我們的猜想雷滚,可以在Activity的onCreate
中打個斷點,看看是從哪里回調(diào)過來的吗坚,如下圖:
從上圖可以很清楚的看到onCreate
方法確實是在Looper.loop()
中去執(zhí)行的祈远,也驗證了我們的猜想。到這里商源,第一個問題也就解了车份,Looper.loop()
方法將主線程阻塞住了,然后系統(tǒng)通過Handler的方式在loop()
中去處理我們的各種回調(diào)牡彻。
再來看第二個問題扫沼,為什么在onCreate
方法中調(diào)用Handler.post()
方法會等到onResume()
之后才被執(zhí)行。從第一個問題我們知道,onCreate
方法是在一個消息中去處理的缎除。那其實有兩種可能严就,一種是在執(zhí)行我們的post
方法之前,插入了一個消息去處理onResume
事件器罐,又或者是onCreate
和onResume
本身就在同一個消息中梢为。
根據(jù)上圖中的方法調(diào)用棧,去ActivityThread源碼中看一下handleLaunchActivity
方法轰坊,這里只給出部分代碼而已铸董。
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
.......
Activity a = performLaunchActivity(r, customIntent);
if (a != null) {
r.createdConfig = new Configuration(mConfiguration);
reportSizeConfigurations(r);
Bundle oldState = r.state;
handleResumeActivity(r.token, false, r.isForward,
!r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);
.....
} else {
.....
}
}
performLaunchActivity
方法最終會調(diào)用到onCreate
方法,這從調(diào)用棧中可以很明顯的看出來衰倦。而從名字上看handleResumeActivity
袒炉,這里最后很有可能會調(diào)用到onResume
方法,具體流程怎么樣樊零,不用跟進去看,畢竟源碼我們不熟孽文,只需在onResume
方法中打個斷點即可驗證我們的猜想驻襟。
從調(diào)用棧中可以看出,onCreate
和onResume
方法是在同一個消息中被處理的芋哭,所以無論你是在onCreate
,onStart
沉衣,還是在onResume
中用Handler往MessageQeue中發(fā)送消息,最終都至少要在onResume
之后才被執(zhí)行到减牺。所以我們可以利用這個特點來做一些需要延遲的操作豌习。
到這里,文章開頭的兩個問題也就都解決了拔疚,從這兩個問題上肥隆,也算是對Handler的機制有了更進一步的理解。