消息延時做了什么特殊處理?
是發送延時,還是消息處理延時?
延時的精度如何?
通常我們使用Handler的消息延時都是調用sendMessageDelayed函數實現的,其中delayMillis是需要延時的毫秒。
通過跟蹤sendMessageDelayed函數可以發現,最終是調用到了上面這個enqueueMessage方法,其中消息隊列mMessages是單鏈表數據結果。
從上面的源碼中可以看到,當前出入的消息msg時,首先判斷mMessages消息隊列中的第一個消息mMessages.prev,要是當前隊列中消息為空,或者msg立即發送,則將該消息插到消息隊列mMessages的頭部;反之,則會在一個for的死循環中遍歷消息隊列并將傳入消息msg插到單鏈表中合適的位置。事實上,消息隊列是按照消息處理的時間when,按照從近到遠的順序排列的,最先要執行的任務放在消息隊列的頭部,依次排列。
從上面可以看到,Handler中sendMessageDelayed方法只是將消息按照要執行的先后順序插入到消息隊列中的,插入好了并不意味著就會按照設定的延時時間處理消息,那Handler時如何延時處理該消息的呢?
首先我們知道,Looper.loop()之后,線程就進入了消息監聽的階段:
當Handler中沒有可用消息的時候,上面代碼會一直阻塞在queue.next()的地方,直到消息返回,才會調用dispatchMessage進行消息的處理,要是返回的msg為空,那么Handler就會結束消息監聽,不再監聽任何消息。
我們來看看queue.next()函數:
上面是阻塞的從消息隊列中獲取可用消息的過程。其中nativePollOnce方法是一個native方法,其內部會根據傳入的nextPollTimeoutMillis,在延遲這么長時間之后喚醒線程從消息隊列中讀取消息,內部調用的是epoll_wait方法。
我們知道當線程中沒有新消息要處理的時候,線程處于休眠狀態,當其他線程向Handler的消息隊列中寫入消息,這一動作并不會喚醒當前線程處理該消息,還需要向線程的eventfd中寫入數據,從而喚醒休眠的線程開始處理數據,此處也是一樣的,nativePollOnce函數內部會調用epoll_wait方法,設置超時時間為nextPollTimeoutMillis,epoll_wait在這個超時時間之后,就會喚醒線程,開始處理消息隊列中的消息。
next方法中,每次會從消息隊列mMessages中獲取鏈表中頭部的消息,要是頭部消息的設定執行的時間要比當前時間大,說明消息隊列中所有的消息都還沒有到可執行的時間,這是因為消息隊列中消息在插入消息隊列的時候,按照執行時間的先后順序已經排序好了。這種情況下,會計算出一個等待時間,傳遞到nativePollOnce函數中,讓native層在這個等待時間之后再喚醒線程讀取消息隊列中的消息,進行消息處理。
Handler的消息延時的實現:
消息隊列在插入消息的時候是按照消息的觸發時間順序排序的,先執行的消息放在單鏈表的頭部,最后執行的消息放在單鏈表的尾部;
在消息執行的過程中,通過native層設置epoll_wait的超時事件,使其在特定時間喚醒線程開始出現消息。
關于延時精度:Handler的延時精度并不高,會受到前一個消息處理時間的影響,因為在Looper.loop()方法中,只有上一個消息被處理完之后,才會去queue中讀取下一個消息。
?