如何生動形象的理解Android Handler訊息處理機制

weixin_34365417發表於2015-11-13

        在一個Android 程式開始執行的時候,會單獨啟動一個Process。預設的情況下,所有這個程式中的Activity,Service,Content Provider,Broadcast Receiver(Android 4大元件)都會跑在這個Process。一個Android 程式預設情況下也只有一個Process,但一個Process下卻可以有許多個Thread。在這麼多Thread當中,有一個Thread,我們稱之為UI Thread。UI Thread在Android程式執行的時候就被建立,是一個Process當中的主執行緒Main Thread,主要是負責控制UI介面的顯示、更新和控制元件互動。在Android程式建立之初,一個Process呈現的是單執行緒模型,所有的任務都在一個執行緒中執行。因此,我們認為,UI Thread所執行的每一個函式,所花費的時間都應該是越短越好。而其他比較費時的工作(訪問網路,下載資料,查詢資料庫等),都應該交由子執行緒去執行,以免阻塞主執行緒,導致ANR。那麼問題來了,UI 主執行緒和子執行緒是怎麼通訊的呢。這就要提到我們這裡要講的Handler機制。

          簡單來說,handler機制被引入的目的就是為了實現執行緒間通訊的。handler一共幹了兩件事:在子執行緒中發出message,在主執行緒中獲取、處理message。聽起來好像so easy,如果面試中讓你闡述下Handler機制,我們這麼回答顯然不是面試官想要的答案。我們忽略了一個最重要的問題:子執行緒何時傳送message,主執行緒何時獲取處理message。

          為了能讓主執行緒“適時”得處理子執行緒所傳送的message,顯然只能通過回撥的方式來實現——開發者只要重寫Handler類中處理訊息的方法,當子執行緒傳送消時,Handler類中處理訊息的方法就會被自動回撥。

           Handler類包含如下方法用於傳送處理訊息

           void handleMessage(Message msg):處理訊息的方法,該方法通常用於被重寫。

           final boolean hasMessage(int what):檢查訊息佇列中是否包含what屬性為指定值的訊息。

           final boolean hasMessage(int what,Object object):檢查訊息佇列中是否包含what屬性為指定值且object屬性為指定物件的訊息。

           sendEmptyMessage(int what)傳送空訊息。

           sendEmptyMessageDelayed(int what,longdelayMillis);指定多少毫秒之後傳送空訊息。

           sendMessage(Message msg)立即傳送訊息。

           sendMessageDelayed(int what,longdelayMillis);指定多少毫秒之後傳送訊息。

           藉助以上方法,我們就可以自由穿梭於主執行緒和子執行緒之中了。

           到這裡就結束了麼?當然沒有。我要講的東西才剛剛開始,要知道訊息處理這件事,不是handler一個人在戰鬥,android的訊息處理有三個核心類:Handler,Looper,和Message。其實還有一個MessageQueue(訊息佇列),但是Message Queue被封裝到Looper裡面了,我們不會直接與Message Queue打交道。

           Looper的字面意思是“迴圈裝置”,它被設計用來使一個普通執行緒變成Looper執行緒。所謂Looper執行緒就是迴圈工作的執行緒。在程式開發中,我們經常會需要一個執行緒不斷迴圈,一旦有新任務則執行,執行完繼續等待下一個任務,這就是Looper執行緒。Looper是用於給一個執行緒新增一個訊息佇列(MessageQueue),並且迴圈等待,當有訊息時會喚起執行緒來處理訊息的一個工具,直到執行緒結束為止。通常情況下不會用到Looper,因為對於Activity,Service等系統元件,Frameworks已經為我們初始化好了執行緒(俗稱的UI執行緒或主執行緒),在其內含有一個Looper,和由Looper建立的訊息佇列,所以主執行緒會一直執行,處理使用者事件,直到某些事件(BACK)退出。

           如果,我們需要新建一個執行緒,並且這個執行緒要能夠迴圈處理其他執行緒發來的訊息事件,或者需要長期與其他執行緒進行復雜的互動,這時就需要用到Looper來給執行緒建立訊息佇列。

           使用Looper也非常的簡單,它的方法比較少,最主要的有四個:

            public static prepare();為執行緒初始化訊息佇列。

            public static myLooper();獲取此Looper物件的引用

            public static loop();讓執行緒的訊息佇列開始執行,可以接收訊息了。

            public void quit();退出具體哪個Looper

            在整個訊息處理機制中,message又叫task,封裝了任務攜帶的資訊和處理該任務的handler,這個很好理解,就不做介紹了。

            說了這麼多,我知道你一定沒聽太明白。沒關係,我再對Handler執行機制做個總結:

           子執行緒(無looper)借用主執行緒(有looper)裡的Hander傳送一條message到主執行緒,這個message會被主執行緒放入message queue,主執行緒裡面有一個looper,在輪詢message queue的時候發現有一條message。呼叫handler訊息處理者,執行handlemessage方法,去處理這個message,就可以在handlemessage的方法裡面更新ui。好像畫面感不是太強,那我舉個例子吧。試想有一個生產方便麵的車間,這個車間有兩條生產線,一條是生產麵餅的,一條是生產調料包的,麵餅的生產線比較先進一點,配備了一個工人叫handler,還配備了一個調料包分類迴圈器。這個車間能生產很多種方便麵,有老壇酸菜,有泡椒鳳爪,有香菇燉雞,有紅燒牛肉等等。其中方便麵的麵餅都是一樣的,只有調料包和包裝袋不一樣,包裝袋是根據調料包來定的。那麼生產線運作起來了:工人Handler把子生產線(子執行緒)上的調料包(message)放到了主生產線(主執行緒)上的調料包分類迴圈器(Looper)上,通過輪詢分類篩選後工人Handler確定這是一包合格的老壇酸菜味調料包,於是工人把老壇酸菜調料包和麵餅放在一塊(sendmessage),告訴主生產線,這是一包老壇酸菜方便麵,請給這包方便麵包一個老壇酸菜的包裝袋(更新UI).

  好了,我想這下你肯定懂了。

         (如有刊誤,歡迎指正)

相關文章