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