android timer后函数继续执行_一切从android的handler说起(三)
?
“ 閱讀本文大概需要4分鐘。”
和小張聊到興起,我就問了android面試界一個眾所周知的問題。
我:之前說到每個線程的looper都在不斷的從message queue里取message來處理,那android系統(tǒng)是如何做到“不斷”二字的?
小張快速回到答:這個我看過一些技術(shù)文章里剖析過源碼,我記得是Looper是在loop()方法里通過for(;;)死循環(huán)里的Message msg = queue.next()這句話來不斷獲取message queue里的下一條message。
我繼續(xù)問道:沒錯,看來你的確接觸過這塊兒的源碼,而且記憶力還挺不錯啊。
緊接著我又問道:我們都知道在UI線程里,系統(tǒng)預(yù)先為我們創(chuàng)建了一個looper,那么UI線程里的looper這個死循環(huán)豈不是占用了所有CPU資源,一打開app豈不是卡出翔?但是我們平時用app很流暢啊,這是怎么回事啊?
聽到這個問題,看小張臉色犯難,感覺當(dāng)時小張的心情是這樣的:
小張:哦,是這樣的 ...
怕什么來什么,躲是躲不過去了,只好硬著頭皮扛下去。
小張沉思片刻,說道:我記得這個循環(huán)比較特殊,并不是普通的死循環(huán),queue.next()是一個可能阻塞線程的操作,也就是說可能會交出CPU執(zhí)行時間片,而不至于導(dǎo)致卡頓或者ANR發(fā)生。
我聽后,感覺有得聊,于是打算趁機(jī)打算再深入一步。
繼續(xù)問道:queue.next()看起來像是去queue里獲取下一條可以處理的message,你能否對這塊兒的邏輯說得詳細(xì)一些呢?
小張:嗯,可以。如果message queue檢查到單鏈表里沒有任何message存在,就會使得線程阻塞在此處,因為沒有任何message可以返回使得loop()方法繼續(xù)下面的處理邏輯,因此也沒有可執(zhí)行的代碼片段,對于CPU來講此線程將會進(jìn)入休眠,就會把時間片分配給其他線程。
如果message queue檢查到單鏈表隊首有message可以立即返回[注1],交給loop()執(zhí)行接下來處理此message 的邏輯,處理完邏輯后。for(;;)又會緊接著循環(huán)到開頭的queue.next()問其要下一條message,如此周而復(fù)始...
聽完小張的回答,我是比較滿意的,畢竟到目前為止,我面的候選人里能回答到這一步的都比較少,心里對小張的印象分提高了不少。
為了考察小張對這個問題的理解到底有多深,我決定打破砂鍋問到底。
我:嗯,很好很好。你的理解基本上是正確的,但這里面有個問題不知道你考慮過沒有,就是當(dāng)message queue在檢查到?jīng)]有message時進(jìn)入了休眠。
那如果此時,用戶此時來一個按鈕點擊事件,點擊事件包裝成的message被handler扔進(jìn)來時,message queue又如何知道,從而立即做出反應(yīng)?
畢竟UI線程是不能錯失任何一個message的,否則對用戶來說就是點擊沒有反應(yīng),對吧?
估計小張聽完感覺內(nèi)心快崩潰了...
小張:這個我只記得queue.next()的源碼里有個nativePollOnce()進(jìn)入了JNI C代碼里,有立即執(zhí)行的message就會立即返回,沒有的話nativePollOnce()無法返回,線程就會進(jìn)入休眠。如果此時有message來時,這里面的邏輯就會喚醒線程,重新進(jìn)行獲取message的邏輯...
我看小張的回答稍顯模糊,估計是到了他理解的極限了,但畢竟離真相只差最后一步了,我懷著試一試的態(tài)度,打算做最后的試探。
于是問道:嗯嗯,能夠理解到這個程度相當(dāng)不錯了。那能夠最后說一下你剛才提到的”喚醒“這塊具體是怎么實現(xiàn)的嗎?
小張故作思考了一下道:這塊兒...不是很了解...
我試圖引導(dǎo)道:看nativePollOnce()這個函數(shù)的名字,有沒有感覺很像Linux的什么機(jī)制?
小張經(jīng)過提示,貌似想起了什么,小聲問道:你說的是Linux的epoll嗎?
到了這一步,我直接攤牌:是的,正是大名鼎鼎的epoll,epoll機(jī)制提供了Linux平臺上最高效的I/O復(fù)用機(jī)制,它能夠在一個地方等待多個文件句柄的I/O事件。
簡而言之,就是android使用pipe管道創(chuàng)建兩個fd文件描述符,nativePollOnce方法中正是承載著這個管道的讀操作,根據(jù)fd知道是管道讀端有可讀事件。
當(dāng)有message來臨時,比如觸摸事件,即會調(diào)用message.enqueueMessage(),添加完message后,調(diào)用了native層的nativeWake方法,就會向管道寫端寫一個“W”字符[注2],這樣就能觸發(fā)管道讀端從epoll_wait函數(shù)返回,從而達(dá)到喚醒線程的目的,從nativePollOnce()處繼續(xù)執(zhí)行下去。
看小張似懂非懂的的表情,我知道這個可能一時半會兒解釋不清楚了。
就微笑著說道:沒事兒,你回答得已經(jīng)挺好的了,關(guān)于pipe/epoll這塊兒的知識點,你可以后面回去查資料了解一下。
小張點了點頭,道:嗯,的確是,對于操作系統(tǒng)的底層知識,的確是我的薄弱點,我一定會加強(qiáng)。
[注1]:這里并非有message即返回,因為此message有可能是由postDelay發(fā)送的延時處理消息,不可立即執(zhí)行,需要等到時間到時才執(zhí)行。關(guān)于此種情況,請見下一篇。
[注2]:關(guān)于這里是如何觸發(fā)的,請等后面的文章。
歡迎轉(zhuǎn)發(fā),關(guān)注公眾號
總結(jié)
以上是生活随笔為你收集整理的android timer后函数继续执行_一切从android的handler说起(三)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 同力重工矿山专用机油玉柴能用吗?
- 下一篇: 检查汽车减震坏没坏要多久?