[英]Android: What does posting a runnable from another thread onto the main thread actually do?
所以,正如標題所說:當你將另一個線程的runnable發布到主線程上時會發生什么?
我已經看到很多問題,詢問你是如何做到的,以及它的基礎知識是如何工作的。 但是,當你在MessageQueue上放置一個runnable時,我很難找到確切的解釋。 肯定是它在Runnable轉彎時運行。 但這是什么時候?
例如:
假設有一個啟動ASync請求的按鈕,請求返回並觸發在MainThread上運行的runnable / callback。 怎么了? runnable被添加到MessageQueue並在'time'時運行。 但是什么時候到了? 如果我在Async請求發布MainThread上的runnable之前按下另一個在MainThread上執行半長阻塞任務的按鈕怎么辦? 是否等到我的阻止按鈕上的邏輯完成? 它會打斷它嗎? 它是否將可運行代碼與我的阻塞代碼按鈕的代碼交織在一起? 究竟發生了什么?
我問的主要原因是,我可以更好地理解我需要考慮哪些因素來防止多線程引起的錯誤。 (特別是影響已刷新頁面的舊請求的情況)
首先,您需要了解Message
類是什么樣的。 Message
對象包含以下字段:
Handler target; // a handler that enqueued the message
long when; // the time at which the message is to be processed
[RUNNABLE] Runnable callback; =
[SWITCHED] int what, int arg1, int arg2, Bundle data...
bool isAsynchronous; // I will talk about it in the end
我用[RUNNABLE]和[SWITCHED]標記的內容表示處理Message
的兩種非重疊方式。 如果callback
不為null,則忽略所有[SWITCHED]字段。 如果callback
為空比Message
由[SWITCHED]字段和無論是在被處理Handler's
被覆蓋的handleMessage()
或handleMessage()
的中Handler.Callback
處理程序用初始化。
MessageQueue
按when
字段排序。 Looper
不會出列消息並處理消息,直到當前時間(由SystemClock.uptimeMillis
測量)大於或等於消息的when
字段中存儲的時間。
當您調用Handler#post(Runnable r)
,會發生以下情況:
從池中獲取Message
( Message
類中的簡單靜態鏈接列表)
您的Runnable
被分配給消息的callback
字段。
when
字段被簡單地設置為當前時間,如果沒有延遲或特定時間傳遞
Message
被排入MessageQueue
。 如果when
早於隊列頭部的那個,它就變成了一個新頭。 如果不是,則將其插入中間,以便MessageQueue
按when
排序
Looper
處於非終止循環中,從隊列中出列消息並按順序處理它們(沒有交織),最終使我們的消息出列,並在最初發布Runnable
的處理程序上調用dispatchMessage()
。
處理程序決定消息是[RUNNABLE]還是[SWITCHED]並相應地處理它。 特別是如果它存在的話,它會在callback
上調用run()
這應該回答你關於在阻塞任務期間發布在UI線程上的Runnable
的行為的問題 - 好吧, 不,它不會中斷正在進行的任務,也不會交織 。 線程上發生的所有事情首先進入MessageQueue
,按鈕單擊或您從其他線程發布的自定義Runnables
。 基本上沒有辦法以其他方式發生: Looper.loop()
只是讓線程忙於for(;;)
循環。
有一些方法可以改變消息的順序。
例如,Looper / Handler框架中有一個有趣的同步屏障概念。 同步障礙是一種慣例,只是一個帶有空target
的Message
(所以它基本上只是一個類似於旗幟的東西,沒有處理程序來調度它)。 如果使用postSyncBarrier()
將其放入隊列,則整個隊列的更改將更改,直到使用removeSyncBarrier()
從隊列中刪除同步屏障。 未標記為isAsynchronous
的Messages
將被忽略,並且根本不會出列和處理。 而是掃描隊列,直到找到isAsynchronous = true
的消息。 然后根據它的時間進行安排,並在時機到來when
進行處理。
另外,你可以調用一個不言自明的Handler#postAtFrontOfQueue()
,如文檔中所指出的那樣
此方法僅用於非常特殊的情況 - 它可能很容易使消息隊列餓死,導致排序問題或具有其他意外的副作用。
我建議你瀏覽所有提到的類的源代碼 。 它讀起來就像一本好書。
MainThread執行了許多其他runnable,例如更新UI,觸摸事件。 '時間'是發布的runnable准備好出列的時間。 如果任何其他runnable出現在它之前,你的runnable將等待。
這里沒有中斷這樣的事情。 你的按鈕將提交一系列runnables,就像從許多不同的線程提交相同數量的runnable一樣。
如果你有一條非短消息(任何包含LONG字對UI不好的話)操作將阻止執行隊列中提交的其他重復任務,大多數情況下經常會證明沒有更新(對於執行不確定的任務)如果是執行需要超過8毫秒的可運行的突發問題,那么根本就是用戶界面。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.