handler 機制的原理是什麼?


雖然是個老問題,但是裡面的可學習的知識點實在太多了,這部分的源碼每次重新讀一遍都會有新的體會,最近學習了下messageQueue中的async和sync類型的message對調度過程產生的影響,進而深入到JNI層面的代碼學習到了JNI(畢竟消息的調度邏輯實現在了JNI層)和java之間的通信的例子(例如mPtr作為指針進行傳遞)。

發現了一個有點早的博客但是沒什麼人提到,我就貼出來一塊兒複習一下,裡面的Barrier and Asynchronous Message部分我覺得講述的很好,對於handler原理系列的源碼的理解有很大的幫助。

Android message handling mechanism (Handler, Looper, MessageQueue and Message)


一、google為什麼設計這套機制

主要是為了解決在非UI線程中更新UI組件比較麻煩的問題。

二、google如何實現這套機制

UI線程中有一個線程專屬的Looper對象,它負責安排所有準備在UI線程上執行的代碼。這裡有兩點技術:實現UI線程專屬的Looper對象用到了java的ThreadLocal技術,想深究請直接看ThreadLocal源碼,不難,真心不難,當然不看也行,只要知道有這麼回事也夠,需要時再看。Looper對象通過消息機制接受系統或者應用的其他線程提交的準備在UI線程上執行的代碼。提交方式是:以該Looper對象為參數創建一個Handler對象,也可以在UI線程中無參數構建一個Handler對象,此時的Handler對象直接就與UI線程的Looper對象綁定。Handler對象負責向Looper對象提交代碼。最直接的提交方法是調用Handler對象的post方法,該方法的參數是一個Runnable對象,代表了要在UI線程上執行的代碼。這樣的方法簡單但是沒法傳遞數據給要執行的代碼,因為構造Runnable對象是沒有參數的。為此,google提供了另一種提交代碼的方法,就是讓Handler對象發送一個消息給Looper對象,這個消息中可以包含一定的數據(消息的what域和obj域就是用來包含數據的),要執行的代碼就是Handler中的handleMessage方法,該方法會收到發送給Looper的消息,進而可以從中取出數據再執行代碼。

以上為原理。

三、一些可以幫助理解原理的細節

因為Looper是給UI線程安排代碼的,所以一個UI線程只能有一個Looper對象,否則多個Looper對象都要在UI線程上安排代碼,解決衝突就是個大難題。因為Looper對象是線程專屬的,所以一個Looper也只能對應一個UI線程。二者是一對一的關係。但是一個Looper對象可以有多個Handler對象向它提交代碼,這並不會引起代碼衝突。因為Looper對象會線性安排在UI線程上待執行的代碼,它通過一個隊列管理各個Handler對象提交的代碼。Looper對象安排執行代碼靠的是它的loop方法。

四、具體使用方法

將一個與UI線程上Looper對象關聯的Handler對象傳給其他線程,其他線程通過這個對象向UI線程上提交代碼。

五、該機制的一般性擴展

實際上,可以為任意一個線程創建一個唯一的Looper對象,這是通過Looper類的靜態方法prepare實現的,然後可以用這個Looper對象創建一個或多個Handler對象,然後就可以用這些Handler對象向該線程提交執行代碼了,google提供了一個HandlerThread類,就是一個已經實現好了Looper對象的Thread,方便你的使用。

如果覺得本文對你有所幫助,希望不要吝惜你的大拇指,給點個贊。


1、我們先說下什麼是Android消息處理機制?

消息處理機制本質:一個線程開啟循環模式持續監聽並依次處理其他線程給它發的消息。

簡單的說:一個線程開啟一個無限循環模式,不斷遍歷自己的消息列表,如果有消息就挨個拿出來做處理,如果列表沒消息,自己就堵塞(相當於wait,讓出cpu資源給其他線程),其他線程如果想讓該線程做什麼事,就往該線程的消息隊列插入消息,該線程會不斷從隊列里拿出消息做處理。

2、Android消息處理機制的工作原理?

打個比方:公司類比App

  • PM 的主要工作是設計產品,寫需求文檔,改需求,中途改需求,提測前改需求...
  • UI 主要工作是UI設計,交互等。
  • RD 工作我就不說了
  • CEO 不解釋。

公司開創之後(App啟動),那麼CEO開始幹活了(主線程【UI線程】啟動),這時候CEO開啟了無限循環工作狂模式,自己的公司沒辦法啊(相當於UI主線程轉成Looper線程【源碼裡面有】)CEO招了一名RD(new Handler 實例)並把告訴PM和UI,如果你們有什麼任務和需求就讓RD(Handler實例)轉告給我(CEO)。RD會把PM和UI的需求(Message)一條條記到CEO的備忘錄里(MessageQueue)。CEO 無限循環的工作就是不斷查看備忘錄,看有什麼任務要做,有任務就從備忘錄一條一條拿出任務來,然後交給這一名RD(Handler 實例)去處理(畢竟CEO 不會寫代碼 囧...)。當然如果備忘錄都做完了,這時候CEO就會去睡覺(線程堵塞【簡單理解成線程wait】,讓出CPU資源,讓其他線程去執行)。但是這個備忘錄有個特殊的功能就是沒有任務的時候突然插入第一條任務(從無到有)就會有鬧鐘功能叫醒CEO起床繼續處理備忘錄。 整個消息處理機制的工作原理基本就是這樣的。後面會有源碼分析,你再來結合這個場景,會更好理解一些。

這裡先給一張Android消息處理機制流程圖和具體執行動畫,如果看不懂沒事,接著往下看(後面會結合Android UI主線程來講解),然後結合著圖和動畫一塊看更能理解整個機制的實現原理。

3、Looper、Handler、MessageQueue,Message作用和存在的意義?

  • Looper

    我們知道一個線程是一段可執行的代碼,當可執行代碼執行完成後,線程生命周期便會終止,線程就會退出,那麼做為App的主線程,如果代碼段執行完了會怎樣?,那麼就會出現App啟動後執行一段代碼後就自動退出了,這是很不合理的。所以為了防止代碼段被執行完,只能在代碼中插入一個死循環,那麼代碼就不會被執行完,然後自動退出,怎麼在在代碼中插入一個死循環呢?那麼Looper出現了,在主線程中調用Looper.prepare()...Looper.loop()就會變當前線程變成Looper線程(可以先簡單理解:無限循環不退出的線程),Looper.loop()方法裡面有一段死循環的代碼,所以主線程會進入while(true){...}的代碼段跳不出來,但是主線程也不能什麼都不做吧?其實所有做的事情都在while(true){...}裡面做了,主線程會在死循環中不斷等其他線程給它發消息(消息包括:Activity啟動,生命周期,更新UI,控制項事件等),一有消息就根據消息做相應的處理,Looper的另外一部分工作就是在循環代碼中會不斷從消息隊列挨個拿出消息給主線程處理。

  • MessageQueue

    MessageQueue 存在的原因很簡單,就是同一線程在同一時間只能處理一個消息,同一線程代碼執行是不具有並發性,所以需要隊列來保存消息和安排每個消息的處理順序。多個其他線程往UI線程發送消息,UI線程必須把這些消息保持到一個列表(它同一時間不能處理那麼多任務),然後挨個拿出來處理,這種設計很簡單,我們平時寫代碼其實也經常這麼做。每一個Looper線程都會維護這樣一個隊列,而且僅此一個,這個隊列的消息只能由該線程處理。

  • Handler

    簡單說Handler用於同一個進程的線程間通信。Looper讓主線程無限循環地從自己的MessageQueue拿出消息處理,既然這樣我們就知道處理消息肯定是在主線程中處理的,那麼怎樣在其他的線程往主線程的隊列里放入消息呢?其實很簡單,我們知道在同一進程中線程和線程之間資源是共享的,也就是對於任何變數在任何線程都是可以訪問和修改的,只要考慮並發性做好同步就行了,那麼只要拿到MessageQueue 的實例,就可以往主線程的MessageQueue放入消息,主線程在輪詢的時候就會在主線程處理這個消息。那麼怎麼拿到主線程 MessageQueue的實例,是可以拿到的(在主線程下mLooper = Looper.myLooper();mQueue = mLooper.mQueue;),但是Google 為了統一添加消息和消息的回調處理,又專門構建了Handler類,你只要在主線程構建Handler類,那麼這個Handler實例就獲取主線程MessageQueue實例的引用(獲取方式mLooper = Looper.myLooper();mQueue = mLooper.mQueue;),Handler 在sendMessage的時候就通過這個引用往消息隊列里插入新消息。Handler 的另外一個作用,就是能統一處理消息的回調。這樣一個Handler發出消息又確保消息處理也是自己來做,這樣的設計非常的贊。具體做法就是在隊列裡面的Message持有Handler的引用(哪個handler 把它放到隊列里,message就持有了這個handler的引用),然後等到主線程輪詢到這個message的時候,就來回調我們經常重寫的Handler的handleMessage(Message msg)方法。

  • Message

    Message 很簡單了,你想讓主線程做什麼事,總要告訴它吧,總要傳遞點數據給它吧,Message就是這個載體。

-------------------------------

以上是我最近剛寫一篇 Android 消息處理機制(Looper、Handler、MessageQueue,Message)

的文章裡面的部分內容摘要,本來還有個消息發送和處理的動畫(不過知乎放不了動畫),可以到我的博客看一下,對於Handler 相關的機制寫得比較詳細,希望可以幫到你!


Android程序啟動後會起一個進程,所有的組件都在這個進程裡面運行。開始這個進程只包含一個線程,叫做UI主線程,負責處理UI界面的顯示更新。對於一些費時的操作(超過5S會卡頓)需要單獨啟動一個子線程去處理。子線程處理完畢將結果通知給UI主線程,主線程得到結果後更新UI界面。子線程與UI主線程的通信在android中使用了消息機制來完成,那麼是怎麼完成的呢?這就和handler 機制的原理,簡而言之言而總之,就是需要兩樣樣古老的東西,消息隊列、輪詢。也就是說,主線程起來以後有一個消息隊列,同時和該隊列配對的有一個輪詢,而子線程有這個消息隊列的引用,那這樣,子線程處理完以後就會向主線程的消息隊列發消息,主線程輪詢自己的隊列,發現有未處理的消息就進行處理。這就是handler的機制,對於handler具體怎麼設計的,不解釋,去查查String 的字元串池


Handler 是一個消息分發對象。而消息分發,有賴於消息循環,也就是 Looper。在一個線程中,Looper 阻塞線程,等待消息構成循環,有了消息,分配到對應的 Handler,讓他進一步分發處理,如是。


剛好畫了UML圖,核心的幾個類如下:

Handler本質是用於跨線程通信的,如果是「子線程執行耗時操作,完畢後通知主線程」,就是我們常說的非同步過程;當然也可以是兩個對等子線程,例如線程A與線程B,要實現線程A擁有消息循環,線程B執行執行某項操作後通知線程A。

可以先讀一讀下面這篇博客。

Android 非同步消息處理機制 讓你深入理解 Looper、Handler、Message三者關係

這個過程可以這樣描述:

1、線程A中new一個Looper對象(同時也在其中new了MessageQueue對象),並封裝到線程的成員變數ThreadLocal.ThreadLocalMap中;

2、線程A中new一個Handler對象(new的過程中獲得當前線程的Looper對象和其中的MessageQueue對象),覆寫其中的dispatchMessage(Message msg)函數;

3、線程A中獲得Looper對象,並獲得其中的MessageQueue對象,然後進入死循環,不斷遍歷MessageQueue;

4、線程B實例化Message,並將target設置為線程A中的Handler,通過線程A的Handler對象,將Message放到Handler的MessageQueue對象中,此時線程A的死循環能獲得這個Message,然後執行了這個Message的回調,也就是Handler的dispatchMessage(Message msg)函數。


CSDN的一篇文章很不錯 Android 非同步消息處理機制 讓你深入理解 Looper、Handler、Message三者關係


推薦閱讀:

Google 可能會在 Android P 中更嚴格限制隱藏 API 的使用
Google 開發者大會紀念 T 恤贈送(全球限量)
如何在Android上發送加密郵件?推薦這四大神器
如何在 Android 上使用思源黑體作為系統字體?

TAG:HandlerSocket | Android |