深入源碼理解Handler機制

前言

  Handler機制老生常談 也是面試必考 最近公司也在面試 發現很多人第三庫都會用 比如熟練運用okhttp,rxjava 但是偏偏Android裏面很重要的Handler機制卻說的模棱兩可,雖說代碼很多 但是流程並不複雜 這次讓我們站在源碼的角度去理解並掌握它

概念 

  Handler機制:主線程和子線程的通信機制 多用在子線程發送消息通知UI線程更新UI
  Looper:輪詢器 AndroidThread中啓動 死循環 一直存在 直到進程銷燬
  MessageQueue:Looper的成員變量 消息隊列 鏈表結構
  Message:MessageQueue的成員變量 一個消息實體類

流程分析

  既然說Handler用於在主線程和子線程傳遞消息的 這裏我們要把握一個關鍵點->消息隊列 有隊列就有入隊和出隊 自然構成了handler收發消息的整個流程,先來看看源碼方法的大概調用過程
   這裏寫圖片描述
  

  1. handler的sendMessage方法發出消息 通過 queue.enqueue方法進入MessageQueue(消息隊列)
  2. Looper的loop方法不停的從MessageQueue裏取消息
  3. 沒消息就阻塞 有消息的話Looper就會調用Message的handler對象的dispathchMessage方法轉發消息
  4. dispathchMessage方法裏面實際調用的handleMessage回調

源碼實現

Handler

 首先是入口方法handler.sendMessage
  
這裏寫圖片描述
 這裏寫圖片描述
 這裏寫圖片描述

 分析:最終會調用queue.enqueueMessage方法 queue就是MessageQueue(消息隊列)
這裏的queue是怎麼來的呢? 看看Handler的構造函數

這裏寫圖片描述
 這裏寫圖片描述
 分析:實際上是Handler初始化的時候就賦了值,queue是Looper裏面的 而mLooper = Looper.myLooper 下面去Looper裏面看看這個方法

Looper

 這裏寫圖片描述
 這裏寫圖片描述
 
 分析: Looper構造函數裏面初始化了MessageQueue,以及當前線程 myLooper方法這裏的sThreadLocal是什麼呢?再看
 這裏寫圖片描述
 原來是Looper持有一個靜態的本地線程(ThreadLocal) 它是java爲解決多線程併發所提供的一種方法。簡單說就是數據隔離
 用在這裏的意義在於->我們可能會有另一個主線程(有別於UI線程)收發消息 多個主線程應該持有自己的Looper對象 保證每個線程的數據隔離 互不干擾
 深入理解可以看http://blog.csdn.net/lufeng20/article/details/24314381
 
 問題又來了,我們知道獲取Looper對象是sTreadLocal.get() 也就是本地線程中取出來的,那麼是什麼時候放進去的呢?也就是說Looper是什麼開始構建出來 並工作的呢?
  我知道沒有Looper輪詢器取消息 handler可以發消息 但絕b收不到消息 所以我們可以想的到 Looper很有可能是在UI進程一啓動 就創建了 我們去Android入口看看:

AndroidThread

 這裏寫圖片描述

 分析:到這裏我們可以感到欣慰了,如果沒有猜錯的話 Looper.prepareMainLooper();就是構建Looper對象的方法

Looper

 這裏寫圖片描述
  果然是這樣的 UI線程會創建一個Looper對象 並set到TreadLocal裏面 我們注意到prepare(false) UI線程這裏傳的是false 而其他線程默認是true 看下它是在那用到的 找到MessageQueue裏面的quit方法

MessageQueue

 這裏寫圖片描述
 分析: MessageQueue裏面的quit 意思是消息沒有處理完 主動退出 而主線程是不予許你這樣操作的 所以傳的false

 大致的我們都清楚了 最後看下Looper.loop()
 這裏寫圖片描述

總結

 最後我們再把流程梳理一遍
 AndroidTread ->Looper.prepareMainLooper() -> Looper.loop()
   1. AndroidThread創建一個UI線程的Looper對象並set到ThreadLocal在Looper的構造函數裏會初始化當前MessageQueue對象和當前線程
   2. handler.sendMessage最終調用當前Looper的MessageQueue對象的enqueueMessage方法將消息壓入消息隊列(MessageQueue)
   3. Looper的loop方法先從ThreadLocal 取出當前線程的Looper對象,得到當前Looper對象的MessageQueue然後輪詢調用MessageQueue的next方法取消息
   4. 如果有Message就調用這個Message的handler對象的dispatchMessage方法轉發消息
   5. 最後在handleMessage回調接受消息

子線程裏面怎麼用Handler接收消息?

 看到這裏我們知道 每個線程都有一個Looper對象去輪詢MessageQueue裏面的消息 然後轉發給handler接收 UI線程在AndroidThread裏面幫我們寫好了創建Looper以及Looper.loop的方法 那麼如果要在子線程中使用Handler 我們就需要手動創建 其實源碼註釋已經給瞭解釋
 這裏寫圖片描述
 Android還給我們提供了HandlerThread這樣一個類 也是這種實現方式

最後

 handler.sendMessage的時候 不是直接new Message而是用的 Message.obtain(),它的內部是一個緩存池 可以避免頻繁創建對象 節省內存
 另外,MessageQueue enqueueMessage入隊方法 以及 next出隊方法的實現 這些涉及到鏈表數據結構
 我這裏主要分析了java層的代碼 源碼裏面還用到很多native的方法 這些需要我們對Android底層C代碼有更深入的研究..

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章