標籤:發送 androi 部分 死迴圈 分享 notifyall body remove 表結構
HandlerThread
1.run()方法
HandlerThread 從繼承關係上看, 它繼承Thread類, 由此可以得知這個類其實是一個線程類,既然是一個線程類, 那麼肯定是要重寫Thread中的run()方法, 所以可以瀏覽下run()方法
從紅色箭頭的三個方法中, 看到有三個方法, Looper.prepare(), Looper.myLooper(), Looper.loop(), 這三個方法其實是, 在子線程建立Handler時所要調用的三個方法, 在HandlerThreadle類中, 和其它類的區別在於建立了一個Looper, 讓它能夠在該線程中建立Handler對象
1.1 run()方法中的Synchronized和notifyAll
通常情況下, 只要出現了synchronized關鍵字, 往往意味著此處會造成安全執行緒問題, 並且這裡調用了Thread中的notifyAll()方法來喚醒其它線程, 此處應該能夠推斷出, 既然調用了notifyAll方法, 那麼很有可能其它地方使用了wait()方法, 具體調用位置請看 getLooper方法
2.getLooper()方法
該方法中有一行注釋, 大概意思是, 如果當這個線程已經被建立了, 但是looper還沒有建立完成, 此時需要讓線程進入wait()狀態, 等待looper對象建立完成, 當looper對象在run()方法中通過Looper.myLooper()建立完成後, 會緊接著調用notifyAll()方法來喚醒wait()狀態下的線程
3. quit()方法和quitSafely()方法
3.1 用途
這兩個方法的用途其實都是終止looper的, 因為每次建立一個HandlerThread對象, 調用run()方法, 就會建立一個Looper對象, 而Looper是一個死迴圈, 這樣會消耗大量的資源, 所以正確的操作方式為, 當介面銷毀的時候, 調用quit()方法或者quitSafely()方法, 將Looper停止掉
3.2 源碼
兩個方法看起來很類似, 區別在於一個調用了looper對象中的quit()方法, 另一個調用了quitSafely()方法, 跟進源碼
圖中的mQueue其實是一個MessageQueue對象, 這兩個方法最終調用的就是MessageQueue中的quit方法,只不過傳入的參數分別是true 和 false
查看MessageQueue中的quit方法
傳入的boolean值最終在這裡判斷, 如果為true ,也就是調用的是quitSafely()方法, 那麼執行的是removeAllFutureMessagesLocked()方法, 如果為false, 那麼調用的就是quit()方法, 最終執行的就是removeAllMessagesLocked()方法.
繼續跟進removeAllFutureMessageLocked()方法, 裡面也會調用方法 removeAllMessageLocked(),
這兩個方法的有一個共同點就是, 當方法被調用時, Looper不會再接收新的Message訊息,
區別在於不帶future的方法, 會將訊息佇列中的所有類型的訊息清空, 無論是延遲發送的還是立即執行的, 而帶future的方法, 只是清除掉了延遲訊息, 對於非延遲訊息, 則會將其交給handler去執行.
圖片上面是源碼的處理,
但是, 為什麼n.when > now 就表示後就可以跳出迴圈,意味著沒有立即執行的訊息了?
這是因為MessageQueue訊息佇列處理方式的關係, 簡單的可以理解為, MessageQueue訊息佇列是按照訊息的處理時間的從早到晚排序的
4. MessageQueue插入訊息的方式簡介
Handler將一個訊息發送出去後, 這個訊息會被插入到訊息佇列, 但並不是無腦的將這個訊息插入到最末尾, 先看下Message中的部分成員屬性
When:是這個訊息的執行時間, 也就是判斷這個訊息是否是一個延遲訊息, 以及延遲多久
Data: 是這個訊息中攜帶的資料, 並且可以看出Message是利用Bundle來處理訊息的
Next: 是一個訊息對象, 上面有一行注釋, 大概意識是有的時候需要用next來儲存一些鏈關係, 這其實是類似鏈表的結構, 鏈表結構中有頭指標 前端節點等概念, 這裡面假設mMessage永遠指向隊列中的第一個Message
當一個新的訊息發送過來時, 分情況分析,
第一種情況, 滿足放在首位的判斷條件, 那麼就讓mMessages來指向這個新插入的訊息即可, 那麼放在首位的條件是什麼? 請看下面的源碼
熟悉handler源碼的應該知道, Handler發送訊息, 無論調用哪個方法, 最終都是由sendMessageAtTime()這個方法來執行, 而Handler的sendMessageAtTime()方法中調用了這個enqueueMessage()方法, 在enqueueMessage()這個方法中, 首條訊息對象mMessage賦值給了 一個Message對象 p, 然後對這個p進行了一些列判斷,
P == null: 這個判斷表示訊息佇列中mMessage對象是否為null, 如果為null, 那麼證明此時整個訊息佇列都是空的, 所以滿足這個條件, 新的訊息可以放到第一個位置
When == 0: when就是 handler發送訊息時傳入過來的訊息觸發時間, 如果為0, 表示這個訊息是個非延遲訊息, 需要立刻執行, 此時新的訊息也要放到第一條,
When < p.when: 也就是handler新發送的這個訊息的觸發時間, 小於第一條訊息的觸發時間, 那麼也要放到第一個位置
只要滿足上面三種情況, 那麼就讓mMessage指向新插入的訊息即可,
源碼
第二種情況
也就是新發送來的訊息msg的觸發時間, 要晚於mMessage的時間, 那麼就會將msg的when和訊息佇列後面的每個時間when做下對比, 直到符合從早到晚的順序為止
可以看出源碼中有一個死迴圈, 會一直判斷訊息佇列的後面是否還有訊息, 以及觸發時間, 當滿足了任意一個條件後, 就會停止迴圈判斷, 然後將新訊息msg插入到隊列中
源碼中可以看出聲明了一個新的訊息對象 prev, 這個對象永遠指向的是msg的前一個訊息
下面是過程圖, 圖中0x1010等假設為訊息的地址, 僅供理解參考用, 不為真真實位址, 所以無論何時, prev永遠記錄的是p的前一個地址
當新的訊息msg找到何時的位置時, 也就是prev.when < msg.when < p.when時, 就會將msg插入到prev之後, p之前這個位置
到此為止, 新的訊息msg就插入到了MessageQueue隊列中了
如有錯誤, 敬請指正, 感激不盡!
Android源碼學習-----HandlerThread