在Android中,主要通過MessageQueue、Looper和Handler三個類來實現Android應用程式的訊息處理。其中,MessageQueue類用來描述訊息佇列;Looper類用來建立訊息佇列,以及進入訊息迴圈;Handler類則用來發送訊息和接收訊息。
本文將主要對Handler進行簡要介紹,並以一個簡單的執行個體示範如何使用Handler即時更新UI。
1.Handler的作用
在Android中,當應用程式啟動時,Android系統會啟動一個主線程(也被稱為UI線程),主要用來管理介面中的UI控制項,對使用者操作進行即時響應。如果我們在UI線程中進行一項非常耗時的操作,比如點擊Button按鈕,進行檔案上傳或下載,那麼在這段時間內介面會出現假死現象(不響應使用者操作),這顯然是使用者體驗極差的。更為嚴重的是,如果在5秒內這個耗時的操作還未結束的話,Android系統會進行錯誤提示“強制關閉”。
所以通常的做法是啟動一個子線程,然後在子線程中來完成這些比較耗時的操作。但是,如果我們想要在子線程中更新UI,那又該如何做呢?
因為在Android中,更新UI只可以在主線程中進行,所以想要在子線程中更新UI,就必須得子線程通知主線程,然後主線程來更新UI。
這一過程正是通過Handler來實現的。
子線程可以通過兩種方式與Handler進行通訊:Message和Runnable。使用Message可以從子線程中傳遞一些參數給主線程,Handler擷取這些資訊並進行相應的處理。使用Runnable則可以直接執行某個處理結果。其實,這兩者的實質都是在Handler的隊列中放入內容,Handler會處理完一個訊息或者執行完某個處理之後再進行下一步操作,這樣就不會出現多個線程同時要求進行UI處理而引發混亂現象。
2.Handler的常用方法
Handler的訊息佇列中的內容(Message或Runnable)可以設定為馬上執行,延遲一定時間再執行或者指定在某個時刻執行。此外,還可以指定將其放在隊列的頭部,表示該內容具有最高優先順序別,立刻執行。
這些要求的設定可以通過以下一些函數來實現:
(1)post(Runnable r);
(2)postAtTime(Runnable r, long uptimeMillis);
(3)postDelayed(Runnable r, long uptimeMillis);
(4)postAtFrontOfQueue(Runnable r);
(5)sendEmptyMessage(int what);
(6)sendMessage(Message msg);
(7)sendMessageAtTime(Message msg, long uptimeMillis);
(8)sendMessageDelayed(Message msg, long uptimeMillis);
(9)sendMessageAtFrontOfQueue(Runnable r);
3.執行個體
在本執行個體中,我們建立了一個Web工程,該Web工程很簡單,僅僅是擷取系統的目前時間,然後顯示出來而已。
在Tomcat上運行該Web工程,能夠看到1所示的運行效果。
圖1 Web工程運行效果
該頁面是通過一個簡單的date.jsp檔案來實現的。
我們現在要做的就是,在Android工程中,通過一個TextView來顯示當前頁面的內容,並且在子線程中每隔1秒鐘,發送一個Message給Handler,Handler收到Message通知後,完成重新整理UI的操作。
下面就來說說每一步的具體實現。
3.1在主線程中啟動一個子線程
首先,我們需要在主線程中啟動一個子線程,這個比較簡單,直接在MainActivity的onCreate()方法中調用如下方法即可:
new Thread(mRunnable).start();
3.2在子線程中發送Message給Handler
在建立子線程時,我們使用了Runnable介面對象mRunnable。這裡,只需要實現Runnable介面,並重寫該介面的run()方法,在run()方法中實現每1秒發送一條Message給Handler即可。具體實現方法如下:
1 /* 2 * Function : 實現run()方法,每1秒發送一條Message給Handler 3 * Author : 部落格園-依舊淡然 4 */ 5 private Runnable mRunnable = new Runnable() { 6 public void run() { 7 while(true) { 8 try { 9 Thread.sleep(1000);10 mHandler.sendMessage(mHandler.obtainMessage());11 } catch (InterruptedException e) {12 e.printStackTrace();13 }14 }15 }16 };
3.3Handler接收Message通知
最後,我們建立一個Handler對象,用來接收Message通知。在收到Message通知後,完成重新整理UI的操作即可。具體實現方法如下:
1 /* 2 * Function : 實現handleMessage()方法,用於接收Message,重新整理UI 3 * Author : 部落格園-依舊淡然 4 */ 5 private Handler mHandler = new Handler() { 6 public void handleMessage(Message msg) { 7 super.handleMessage(msg); 8 refreshUI(); 9 }10 };
3.4重新整理UI
由以上的代碼可以看出,重新整理UI的操作,我們是放在refreshUI()方法中來完成的。refreshUI()方法的實現也很簡單,調用HttpUtils工具類中的getInputStream()方法,獲得圖1所示Web工程的頁面內容輸入資料流,再將該輸入資料流轉化為字串,放入TextView控制項中進行顯示即可。具體實現方法如下:
1 /* 2 * Function : 重新整理UI 3 * Author : 部落格園-依舊淡然 4 */ 5 private void refreshUI() { 6 try { 7 InputStream inputStream = HttpUtils.getInputStream(); 8 String resultData = HttpUtils.getResultData(inputStream); 9 mTextView.setText(resultData);10 } catch (IOException e) {11 e.printStackTrace();12 }13 }
3.5執行個體效果
最後,運行該Android工程,運行效果2所示,可以看到,目前時間每隔1秒會重新整理一次,能夠始終保持與圖1所示頁面的時間同步。
圖2 運行效果