Handler.post()方法的執(zhí)行時機

平時使用Handler的時候绅作,我們都知道調(diào)用了Handler.sendMessage()方法后怔鳖,消息會在handleMessage()中被處理,或者調(diào)用了Handler.post()之后,Runnable會被在一定的時機下得到執(zhí)行宽气。但至于什么時候,可能就不會去在意這些細節(jié)了潜沦。雖然很久前就看過了Handler的機制萄涯,但那時候是為了學(xué)習(xí)而學(xué)習(xí),一旦跟實際聯(lián)系起來就脫節(jié)了唆鸡。比如說:

  1. 都知道Android的main方法在ActivityThread類中涝影,并且在該方法里面調(diào)用了Looper.loop(); 讓主線程進入了死循環(huán),但是我們平時執(zhí)行的代碼也是在主線程的争占,他們是怎么跳過這個死循環(huán)去執(zhí)行的燃逻?
  2. 有時候需要在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

從上圖可以很清楚的看到onCreate方法確實是在Looper.loop()中去執(zhí)行的祈远,也驗證了我們的猜想。到這里商源,第一個問題也就解了车份,Looper.loop()方法將主線程阻塞住了,然后系統(tǒng)通過Handler的方式在loop()中去處理我們的各種回調(diào)牡彻。

再來看第二個問題扫沼,為什么在onCreate方法中調(diào)用Handler.post()方法會等到onResume()之后才被執(zhí)行。從第一個問題我們知道,onCreate方法是在一個消息中去處理的缎除。那其實有兩種可能严就,一種是在執(zhí)行我們的post方法之前,插入了一個消息去處理onResume事件器罐,又或者是onCreateonResume本身就在同一個消息中梢为。

根據(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方法中打個斷點即可驗證我們的猜想驻襟。

onResume

從調(diào)用棧中可以看出,onCreateonResume方法是在同一個消息中被處理的芋哭,所以無論你是在onCreate,onStart沉衣,還是在onResume中用Handler往MessageQeue中發(fā)送消息,最終都至少要在onResume之后才被執(zhí)行到减牺。所以我們可以利用這個特點來做一些需要延遲的操作豌习。

到這里,文章開頭的兩個問題也就都解決了拔疚,從這兩個問題上肥隆,也算是對Handler的機制有了更進一步的理解。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末稚失,一起剝皮案震驚了整個濱河市栋艳,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌句各,老刑警劉巖吸占,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異凿宾,居然都是意外死亡矾屯,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進店門初厚,熙熙樓的掌柜王于貴愁眉苦臉地迎上來件蚕,“玉大人,你說我怎么就攤上這事≈枳” “怎么了绪杏?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長纽绍。 經(jīng)常有香客問我蕾久,道長,這世上最難降的妖魔是什么拌夏? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任僧著,我火速辦了婚禮,結(jié)果婚禮上障簿,老公的妹妹穿的比我還像新娘盹愚。我一直安慰自己,他們只是感情好站故,可當(dāng)我...
    茶點故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布皆怕。 她就那樣靜靜地躺著,像睡著了一般西篓。 火紅的嫁衣襯著肌膚如雪愈腾。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天岂津,我揣著相機與錄音虱黄,去河邊找鬼。 笑死吮成,一個胖子當(dāng)著我的面吹牛橱乱,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播粱甫,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼泳叠,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了魔种?” 一聲冷哼從身側(cè)響起析二,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎节预,沒想到半個月后叶摄,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡安拟,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年蛤吓,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片糠赦。...
    茶點故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡会傲,死狀恐怖锅棕,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情淌山,我是刑警寧澤裸燎,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站泼疑,受9級特大地震影響德绿,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜退渗,卻給世界環(huán)境...
    茶點故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一移稳、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧会油,春花似錦个粱、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至嫂冻,卻和暖如春梭稚,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背絮吵。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留忱屑,地道東北人蹬敲。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像莺戒,于是被迫代替她去往敵國和親伴嗡。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,592評論 2 353

推薦閱讀更多精彩內(nèi)容

  • 1. ANR異常 Application No Response:應(yīng)用程序無響應(yīng)从铲。在主線程中瘪校,是不允許執(zhí)行耗時的操...
    JackChen1024閱讀 1,382評論 0 3
  • Handler是Android消息通訊當(dāng)中最常用的方式之一。本篇小文將會從Handler的源碼角度去淺析Handl...
    黑色小老虎丶閱讀 1,389評論 0 10
  • 異步消息處理線程啟動后會進入一個無限的循環(huán)體之中名段,每循環(huán)一次阱扬,從其內(nèi)部的消息隊列中取出一個消息,然后回調(diào)相應(yīng)的消息...
    cxm11閱讀 6,424評論 2 39
  • 美圖欣賞 Java伸辟、Android知識點匯集 Java集合類 ** Java集合相關(guān)的博客** java面試相關(guān) ...
    ElvenShi閱讀 1,738評論 0 2
  • 版權(quán)聲明:本文為博主原創(chuàng)文章信夫,未經(jīng)博主允許不得轉(zhuǎn)載窃蹋。 PS:轉(zhuǎn)載請注明出處作者: TigerChain地址: ht...
    TigerChain閱讀 679評論 0 7