使用Handler搭配Thread

来源:互联网 发布:中国游戏中心mac版 编辑:程序博客网 时间:2024/05/17 09:31


當你希望使用Thread來改變UI View內容的時候就必須要搭配Handler來進行存取。

上圖是程式用的layout的解析圖,一開始是以一個ImageView的黑色圓圈提醒使用者那裡有ProgressBar,而真正的ProgressBar在初始化時則是隱藏的,再開始執行之後才經由Handler將它設定為可見,而TextView則會隨著程式的運行不斷的更新內容,提醒使用者進度。

layout/ ui.xml

<?xml version="1.0" encoding="utf-8"?><RelativeLayout  xmlns:android="http://schemas.android.com/apk/res/android"  android:orientation="vertical"  android:layout_width="fill_parent"  android:layout_height="fill_parent">    <ImageView         android:id="@+id/ProgressDialogSample_ImageView_pImg"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_centerInParent="true"        android:background="@drawable/progress_circular_background"    />    <ProgressBar        android:id="@+id/ProgressDialogSample_ProgressBar_pBar"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_centerInParent="true"        android:visibility="invisible"    />   <TextView        android:id="@+id/ProgressDialogSample_TextView_desc"        android:layout_width="wrap_content"         android:layout_height="wrap_content"        android:layout_centerHorizontal="true"        android:layout_below="@id/ProgressDialogSample_ProgressBar_pBar"        android:text="未執行任何步驟。"    /></RelativeLayout>

package iamshiao.sample;import android.app.Activity;import android.os.Bundle;import android.os.Handler;import android.os.Message;import android.view.View;import android.widget.ImageView;import android.widget.ProgressBar;import android.widget.TextView;public class ProgressDialogSample extends Activity {        private final int step1 = 1, step2 = 2, step3 = 3, finish = 4;    private Thread thread = null;        @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.ui);                //建構執行緒        thread = new Thread(){             @Override            public void run(){                 try{                    doStep1();                    doStep2();                    doStep3();                    Thread.sleep(3000);                    Message msg = new Message();                    msg.what = finish;                    uiMessageHandler.sendMessage(msg);                }catch (Exception e){                    e.printStackTrace();                }finally{                }            }        };        //開始執行執行緒        thread.start();            }        private void doStep1() throws InterruptedException{        Thread.sleep(3000);        Message msg = new Message();        msg.what = step1;        uiMessageHandler.sendMessage(msg);    }        private void doStep2() throws InterruptedException{        Thread.sleep(3000);        Message msg = new Message();        msg.what = step2;        uiMessageHandler.sendMessage(msg);    }        private void doStep3() throws InterruptedException{        Thread.sleep(3000);        Message msg = new Message();        msg.what = step3;        uiMessageHandler.sendMessage(msg);    }        //宣告Handler並同時建構隱含類別實體    Handler uiMessageHandler = new Handler(){        @Override        public void handleMessage(Message msg){            //讀出ui.xml中的進度光棒            final ProgressBar pBar =                 (ProgressBar)findViewById(                    R.id.ProgressDialogSample_ProgressBar_pBar);            pBar.setVisibility(View.VISIBLE); //開始後設為可見            //讀出ui.xml中用以表示未開始的圖案            final ImageView pImg =                 (ImageView)findViewById(R.id.ProgressDialogSample_ImageView_pImg);            pImg.setVisibility(View.INVISIBLE); //開始後設為不可見            //讀出ui.xml中的描述用TextView            TextView tv =                 (TextView)findViewById(R.id.ProgressDialogSample_TextView_desc);                        switch (msg.what){                case step1:                    tv.setText(R.string.processing_step1);                    break;                case step2:                    tv.setText(R.string.processing_step2);                    break;                case step3:                    tv.setText(R.string.processing_step3);                    break;                case finish:                    tv.setText(R.string.finish);                    pBar.setVisibility(View.INVISIBLE);                    pImg.setVisibility(View.VISIBLE);                    tv.setText("完成。");                    thread.interrupt();                    break;            }                        super.handleMessage(msg);        }    };}

執行緒的部分與前篇大致相同,但由於其中不能直接執行 

tv.setText(R.string.processing_step1);

這類UI View存取的動作,所以加入了Handler來控制UI View,Handler的運作是利用handleMessage這個事件來監聽執行緒是否有送出Message,如果有的話利用Message的what屬性等機制來判別要進行什麼動作。 例如程式碼中我宣告了4個常數 

private final int step1 = 1, step2 = 2, step3 = 3, finish = 4;private void doStep1() throws InterruptedException{    Thread.sleep(3000);    Message msg = new Message();    msg.what = step1;    uiMessageHandler.sendMessage(msg);}

表示執行緒進行的步驟進度,而執行緒執行副程式doStep1~3的這個過程中,副程式在sleep完成後就會利用Handler變數的sendMessage來送出對應其步驟的Message變數。 

//宣告Handler並同時建構隱含類別實體Handler uiMessageHandler = new Handler(){    @Override    public void handleMessage(Message msg){        ...        switch (msg.what){            case step1:                tv.setText(R.string.processing_step1);                break;            ...        }        super.handleMessage(msg);    }};

最後handleMessage事件再接聽到sendMessage之後就利用Message變數的what屬性之類的機制來判讀、執行對應動作。

重點提醒,使用Thread&Handler要特別注意...

  • handleMessage必須有@Override標註表示覆寫。
  • 每個Message變數必須重新new,只改動what內容仍會被視為同樣的Message,而造成非預期的錯誤。
Thread.sleep(3000);Message msg = new Message();msg.what = step1;uiMessageHandler.sendMessage(msg);Thread.sleep(3000);msg.what = step2; //X 同Message實體只變更what就重複使用會造成非預期錯誤。uiMessageHandler.sendMessage(msg);Thread.sleep(3000);msg = new Message(); //O Message有重新建構實體才正確。msg.what = step3;uiMessageHandler.sendMessage(msg);

  • 任何UI View的控制應該在handleMessage中進行,而不是直接在Thread中進行。

補充
Message變數也可以透過setData方法來攜帶Bundle實體傳遞資料。 
Message msg = new Message();Bundle data = new Bundle();data.putString("data", "data");msg.setData(data);msg.what = step2;uiMessageHandler.sendMessage(msg);case step2:   tv.setText(msg.getData().getString("data"));


转载地址:http://iamshiao.blogspot.com/2010/12/androidhandlerthread.html



原创粉丝点击