Android 有一種叫消息隊列的說法,這裏咱們能夠這樣理解:假如一個隧道就是一個消息隊列,那麼裏面的每一部汽車就是一個一個消息,這裏咱們先忽略掉超車等種種因素,只那麼先進隧道的車將會先出,這個機制跟咱們android 的消息機制是同樣的。java
角色描述android
1. Looper:(至關於隧道) 一個線程能夠產生一個Looper 對象,由它來管理此線程裏的Message Queue( 車隊,消息隧道) 。算法
2. Handler: 你能夠構造Handler 對象來與Looper 溝通,以便push 新消息到Message Queue 裏;或者接收Looper( 從Message Queue 取出) 所送來的消息。編程
3. Message Queue( 消息隊列): 用來存放線程放入的消息。安全
4. 線程:UI thread 一般就是main thread ,而Android 啓動程序時會替它創建一個Message Queue 。數據結構
每個線程裏可含有一個Looper 對象以及一個MessageQueue 數據結構。在你的應用程序裏,能夠定義Handler 的子類別來接收Looper 所送出的消息。多線程
在你的Android 程序裏,新誕生一個線程,或執行 (Thread) 時,並不會自動創建其Message Loop 。框架
Android 裏並無Global 的Message Queue 數據結構,例如,不一樣APK 裏的對象不能透過Massage Queue來交換訊息(Message) 。異步
例如:線程A 的Handler 對象能夠傳遞消息給別的線程,讓別的線程B 或C 等能送消息來給線程A( 存於A的Message Queue 裏) 。ide
線程A 的Message Queue 裏的訊息,只有線程A 所屬的對象能夠處理。
使用Looper.myLooper 能夠取得當前線程的Looper 對象。
使用mHandler = new EevntHandler(Looper.myLooper()); 可用來構造當前線程的Handler 對象;其中,EevntHandler 是自已實現的Handler 的子類別。
使用mHandler = new EevntHandler(Looper.getMainLooper()); 可誕生用來處理main 線程的Handler 對象;其中,EevntHandler 是自已實現的Handler 的子類別。
1. 同線程內不一樣組件間的消息傳遞
Looper 類用來管理特定線程內對象之間的消息交換(Message Exchange) 。你的應用程序能夠產生許多個線程。而一個線程能夠有許多個組件,這些組件之間經常須要互相交換訊息。若是有這種須要,您能夠替線程構造一個Looper 對象,來擔任訊息交換的管理工做。Looper 對象會創建一個MessageQueue 數據結構來存放各對象傳來的消息( 包括UI 事件或System 事件等) 。
每個線程裏可含有一個Looper 對象以及一個MessageQueue 數據結構。在你的應用程序裏,能夠定義Handler 的子類別來接收Looper 所送出的消息。
同線程不一樣組件之間的消息傳遞:
舉例:
說明:
此程序啓動時,當前線程( 即主線程, main thread) 已誕生了一個Looper 對象,而且有了一個MessageQueue數據結構。
調用Looper 類別的靜態myLooper() 函數,以取得目前線程裏的Looper 對象.
構造一個MyHandler 對象來與Looper 溝通。Activity 等對象能夠藉由MyHandler 對象來將消息傳給Looper ,而後放入MessageQueue 裏;MyHandler 對象也扮演Listener 的角色,可接收Looper 對象所送來的消息。
先構造一個Message 對象,並將數據存入對象裏。
就透過mHandler 對象而將消息m 傳給Looper ,而後放入MessageQueue 裏。
此時,Looper 對象看到MessageQueue 裏有消息m ,就將它廣播出去,mHandler 對象接到此訊息時,會呼叫其handleMessage() 函數來處理,因而輸出"This my message!" 於畫面上
角色綜述(回顧)
1. UI thread 一般就是main thread ,而Android 啓動程序時會替它創建一個MessageQueue 。
2. 固然須要一個Looper 對象,來管理該MessageQueue 。
3. 咱們能夠構造Handler 對象來push 新消息到Message Queue 裏;或者接收Looper( 從Message Queue取出) 所送來的消息。
4. 線程A 的Handler 對象能夠傳遞給別的線程,讓別的線程B 或C 等能送訊息來給線程A( 存於A 的Message Queue 裏) 。
5. 線程A 的Message Queue 裏的消息,只有線程A 所屬的對象能夠處理。
子線程傳遞消息給主線程
舉例
說明:
Android 會自動替主線程創建Message Queue 。在這個子線程裏並無創建Message Queue 。因此,myLooper 值爲null ,而mainLooper 則指向主線程裏的Looper 。因而,執行到:
此mHandler 屬於主線程。
就將m 消息存入到主線程的Message Queue 裏。mainLooper 看到Message Queue 裏有訊息,就會做出處理,因而由主線程執行到mHandler 的handleMessage() 來處理消息。
在Android 下面也有多線程 的概念,在C/C++中,子線程能夠是一個函數 ,通常都是一個帶有循環的函數,來處理某些數據 ,優先線程只是一個複雜的運算過程,因此可能不須要while循環,運算完成,函數結束,線程就銷燬。對於那些須要控制的線程,通常咱們都是和互斥鎖相互關聯,從而來控制線程的進度,通常咱們建立子線程,一種線程是很常見的,那就是帶有消息循環的線程。
消息循環是一個頗有用的線程方式,曾經本身用C在Linux下面實現一個消息循環的機制 ,往消息隊列裏添加數據,而後異步的等待消息的返回。當消息隊列爲空的時候就會掛起線程,等待新的消息的加入。這是一個很通用的機制。
在 Android,這裏的線程分爲有消息循環的線程和沒有消息循環的線程,有消息循環的線程通常都會有一個Looper,這個事android的新概念。我 們的主線程(UI線程)就是一個消息循環的線程。針對這種消息循環的機制,咱們引入一個新的機制Handle,咱們有消息循環,就要往消息循環裏面發送相 應的消息,自定義 消息通常都會有本身對應的處理,消息的發送和清除,消息的的處理,把這些都封裝在Handle裏面,注意Handle只是針對那些有Looper的線程,不論是UI線程仍是子線程,只要你有Looper,我就能夠往你的消息隊列裏面添加東西,並作相應的處理。
可是這裏還有一點,就是隻要是關於UI相關的東西,就不能放在子線程中,由於子線程是不能操做UI的,只能進行數據、系統 等其餘非UI的操做。
那麼什麼狀況下面咱們的子線程才能看作是一個有Looper的線程呢?咱們如何獲得它Looper的句柄呢?
Looper.myLooper(); //得到當前的Looper
Looper.getMainLooper () ; //得到UI線程的Lopper
咱們看看Handle的初始化函數,若是沒有參數,那麼他就默認使用的是當前的Looper,若是有Looper參數,就是用對應的線程的Looper。
如 果一個線程中調用Looper.prepare(),那麼系統就會自動的爲該線程創建一個消息隊列,而後調用 Looper.loop();以後就進入了消息循環,這個以後就能夠發消息、取消息、和處理消息。這個如何發送消息和如何處理消息能夠再其餘的線程中經過 Handle來作,但前提是咱們的Hanle知道這個子線程的Looper,可是你若是不是在子線程運行 Looper.myLooper(),通常是得不到子線程的looper的。
因此不少人都是這樣作的:我直接在子線程中新建handle,而後在子線程中發送消息,這樣的話就失去了咱們多線程的意義了。
可讓其餘的線程來控制咱們的handle,能夠把 private EHandler mHandler ;放在外面,這樣咱們的發消息和處理消息均可以在外面來定義,這樣增長程序 代碼 的美觀,結構更加清晰。
對如任何的Handle,裏面必需要重載一個函數
public void handleMessage(Message msg)
這個函數就是咱們的消息處理,如何處理,這裏徹底取決於你,而後經過 obtainMessage和 sendMessage等來生成和發送消息, removeMessages(0)來清除消息隊列。Google 真是太智慧了,這種框架 的產生,咱們寫代碼更加輕鬆了。
有的時候,咱們的子線程想去改變UI了,這個時候千萬不要再子線程中去修改,得到UI線程的Looper,而後發送消息便可。
咱們看看Goole Music App的源代碼 。
在MediaPlaybackActivity.java 中,咱們能夠看一下再OnCreate中的有這樣的兩句:
很 明顯這兩句,是構建了一個子線程。而且這個子線程仍是Looper的子線程,這裏很牛逼的使用了 mAlbumArtWorker.getLooper()這個函數,由於咱們知道,咱們可以獲得子線程的Looper的途徑只有一個:就是在子線程中調用 Looper.myLooper (),而且這個函數還要在咱們perpare以後調用才能獲得正確的Looper,可是他這裏用了一個這樣的什麼東東 getLooper,不知道它是如何實現的?
這裏有一個大概的思路,咱們在子線程的的prepare以後調用 myLooper ()這個方法,而後保存在一個成員變量中,這個getLooper就返回這個東西,可是這裏會碰到多線程的一個很突出的問題,同步。咱們在父線程中調用 mAlbumArtWorker.getLooper(),可是想要這個返回正確的looper就必需要求咱們的子線程運行了prepare,可是這個東 西實在子線程運行的,咱們如何保證呢?
咱們看Google是如何實現的?
我 們知道,一個線程類的構造函數是在主線程中完成的,因此在咱們的 Worker的構造函數中咱們創佳一個線程,而後讓這個線程運行,這一這個線程的建立是指定一個 Runnable,這裏就是咱們的Worker自己,在主線程調用 t.start();,這後,咱們子線程已經建立,而且開始執行work的run方法。而後下面的代碼很藝術:
咱們開始等待咱們的子線程給mLooper賦值,若是不賦值咱們就繼續等,而後咱們的子線程在運行run方法以後,在給 mLooper賦值以後,通知worker夠着函數中的wait,而後咱們的構造函數才能完成,因此咱們說:
這句自己就是阻塞的,它建立了一個子線程,開啓了子線程,而且等待子線程給mLooper賦值,賦值完成以後,這個函數才返回,這樣才能保證咱們的子線程的Looper的獲取 絕對是正確的,這個構思頗有創意。值得借鑑。
本文主要介紹Android的Handler的使用方法。Handler能夠發送Messsage和Runnable對象到與其相關聯的線程的消息隊列。每一個Handler對象與建立它的線程相關聯,而且每一個Handler對象只能與一個線程相關聯。
1. Handler通常有兩種用途:
1)執行計劃任務,你能夠再預約的實現執行某些任務,能夠模擬定時器。
2)線程間通訊。在Android的應用啓動時,會 建立一個主線程,主線程會建立一個消息隊列來處理各類消息。
當你建立子線程時,你能夠再你的子線程中拿到父線程中建立的Handler對象,就能夠經過該 對象向父線程的消息隊列發送消息了。因爲Android要求在UI線程中更新界面,所以,能夠經過該方法在其它線程中更新界面。
◆ 經過Runnable在子線程中更新界面
1)在onCreate中建立Handler
2) 構建Runnable對象,在runnable中更新界面,此處,咱們修改了TextView的文字.此處須要說明的是,Runnable對象能夠再主線程中建立,也能夠再子線程中建立。咱們此處是在子線程中建立的。
3) 建立子線程,在線程的run函數中,咱們向主線程的消息隊列發送了一個runnable來更新界面。
◆ 用Message在子線程中來更新界面
用Message更新界面與Runnable更新界面相似,只是須要修改幾個地方。
1) 實現本身的Handler,對消息進行處理
2) 在新的線程中發送消息
AsyncTask實際上就是一個線程池,AsyncTask在代碼上比handler要輕量級別,而實際上要比handler更耗資源,由於AsyncTask底層是一個線程池!而Handler僅僅就是發送了一個消息隊列,連線程都沒有開。
可是,若是異步任務的數據特別龐大,AsyncTask這種線程池結構的優點就體現出來了。
android的ui線程操做並非安全的,而且和用戶直接進行界面交互的操做都必須在ui線程中進行才能夠。這種模式叫作單線程模式。
咱們在單線程模式下編程必定要注意:不要阻塞ui線程、確保只在ui線程中訪問ui組件
當咱們要執行一個複雜耗時的算法而且最終要將計算結果反映到ui上時,咱們會發現,咱們根本沒辦法同時保證上面的兩點要求;咱們確定會想到開啓一個新的線程,讓這個複雜耗時的任務到後臺去執行,可是執行完畢了呢?咱們發現,咱們沒法再與ui進行交互了。
爲了解決這種狀況,android爲咱們提供了不少辦法。
1)handler和message機制:經過顯示的拋出、捕獲消息與ui進行交互;
2)Activity.runOnUiThread(Runnable):若是當前線程爲ui線程,則當即執行;不然將參數中的線程操做放入到ui線程的事件隊列中,等待執行。
3)View.post(Runnable):將操做放入到message隊列中,若是放入成功,該操做將會在ui線程中執行,並返回true,不然返回false
4)View.postDelayed(Runnable, long)跟第三條基本同樣,只不過添加了一個延遲時間。
5)android1.5之後爲咱們提供了一個工具類來搞定這個問題AsyncTask.
AsyncTask是抽象類,定義了三種泛型類型 Params,Progress,Result。
Params :啓動任務執行的輸入參數,好比HTTP請求的URL
Progress: 後臺任務執行的百分比。
Result :後臺執行任務最終返回的結果,好比String
用程序調用,開發者須要作的就是實現這些方法。
1) 子類化AsyncTask
2) 實現AsyncTask中定義的下面一個或幾個方法
onPreExecute(),該方法將在執行實際的後臺操做前被UI thread調用。能夠在該方法中作一些準備工做,如在界面上顯示一個進度條。
doInBackground(Params…),將在onPreExecute 方法執行後立刻執行,該方法運行在後臺線程中。這裏將主要負責執行那些很耗時的後臺計算工做。能夠調用 publishProgress方法來更新實時的任務進度。該方法是抽象方法,子類必須實現。
onProgressUpdate(Progress…),在publishProgress方法被調用後,UI thread將調用這個方法從而在界面上展現任務的進展狀況,例如經過一個進度條進行展現。
onPostExecute(Result),在doInBackground 執行完成後,onPostExecute 方法將被UI thread調用,後臺的計算結果將經過該方法傳遞到UI thread.
爲了正確的使用AsyncTask類,如下是幾條必須遵照的準則:
1) Task的實例必須在UI thread中建立
2) execute方法必須在UI thread中調用
3) 不要手動的調用onPreExecute(), onPostExecute(Result),doInBackground(Params…), onProgressUpdate(Progress…)這幾個方法
4) 該task只能被執行一次,不然屢次調用時將會出現異常