《Android》『Handler』- 如何在 Thread 中透過 Handler 傳遞訊息以更新UI介面
《Android Developers 參考文獻》
《簡單介紹》
在撰寫 Android 程式的過程中,常常會碰到一些需要快速更新UI介面的功能需求,這時候如果我們直接將更新UI的程式寫在主線程上,會發現UI一動也不動,直到所有運算處理完,才顯示最後的UI變動結果,若是運算的量再稍微大一點,甚至會產生ANR(Application is Not Responding)的對話框。
這是因為主線程會優先處理邏輯運算的關係,因此,在處理這種需要大量更新UI的需求的時候,我們通常會利用新增一個子線程(Thread)的方式,讓這個子線程來協助處理 UI 更新的相關運算,那麼這個新的線程該如何與主線程溝通呢?這個時候,Handler 就派上用場了!
《Handler 的使用方式》
首先,我們先宣告一個名為 Messages 的類別,用來定義 Handler 回傳的參數所代表的意義。這邊我們以實作一個 ProgressBar 進度條為例,分別定義了 StartProgress、Progressing、ProgressComplete 三種不同的狀態。
1 2 3 4 5 6 |
public class Messages { public static final int StartProgress = 0; public static final int Progressing = 1; public static final int ProgressComplete = 2; } |
Messages.class
接著,宣告一個繼承 Handler 的類別,自訂其名稱為 MainHandler,程式碼片段如下 –
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
public class MainHandler extends Handler { @Override public void handleMessage(Message msg) { if(msg.what == Messages.StartProgress) { mProgress.setVisibility(View.VISIBLE); } else if(msg.what == Messages.Progressing) { Bundle bData = msg.getData(); int progressValue = bData.getInt("progress_value"); mProgress.setProgress(progressValue); } else if(msg.what == Messages.ProgressComplete) { mProgress.setVisibility(View.INVISIBLE); } } } |
MainHandler.class
我們自訂了一個名為 MainHandler 的類別,並在裡面的 handleMessage(Message msg) 中,定義了接收到不同的 msg.what 所要執行的動作,其中 msg 亦可以透過夾帶 bundle 的方式,帶入所需的參數。
最後,我們新增一個子線程,並宣告一個基於 MainHandler 類別的物件 mMainHandler,程式碼片段如下 –
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
private MainHandler mMainHandler = new MainHandler(); new Thread(new Runnable() { int i = 0; @Override public void run() { mMainHandler.sendEmptyMessage(Messages.StartProgress); //透過 sendEmptyMessage 告知現在的狀態 while(i != 100) { try { Message message = new Message(); message.what = Messages.Progressing; Bundle bundle = new Bundle(); bundle.putInt("progress", i); message.setData(bundle); mMainHandler.sendMessage(message); Thread.sleep(500); i++; } catch(Exception e) {e.printStackTrace();} } mMainHandler.sendEmptyMessage(Messages.ProgressComplete); } }).start(); |
Thread 寫法一
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
private MainHandler mMainHandler = new MainHandler(); public class mThread extends Thread { int i = 0; @Override public void run() { mMainHandler.sendEmptyMessage(Messages.StartProgress); //透過 sendEmptyMessage 告知現在的狀態 while(i != 100) { try { Message message = new Message(); message.what = Messages.Progressing; Bundle bundle = new Bundle(); bundle.putInt("progress", i); message.setData(bundle); mMainHandler.sendMessage(message); Thread.sleep(500); i++; } catch(Exception e) {e.printStackTrace();} } mMainHandler.sendEmptyMessage(Messages.ProgressComplete); } } . . . new mThread.start(); |
Thread 寫法二
從程式碼來看,我們於 Thread 一開始便傳送了 Messages.StartProgress 這個訊息,告知 MainHandler 將 mProgress 顯示出來,接著透過夾帶 Bundle 的方式,將用來顯示進度值的 i 參數傳給 MainHandler 處理,最後當進度條達到 100 的時候,將 mProgress 隱藏。
這邊要注意的是,我們平常若是不必夾帶參數等資訊,用的都是 sendEmptyMessage(),直接將狀態訊息傳給 MainHandler 即可,但若我們要夾帶資訊呢?這時就需要透過 Message message = new Message() 先將 message 新增出來,再透過 message.setData(bundle) 的方式將 bundle 帶入,接著用 sendMessage(message) 將帶有參數的資訊傳送出去。
從以上步驟,我們可以很輕易的將 UI 運算從主線程中抽離出來,交給子線程去處理,再透過 sendEmptyMessage() 的方式,通知主線程即時更新 UI 介面。
臉書留言
一般留言