Android Handler介紹

前言

  學習android一段時間了,爲了進一步瞭解android的應用是如何設計開發的,決定詳細研究幾個開源的android應用。從一些開源應用中吸取點東西,一邊進行量的積累,一邊探索android的學習研究方向。這裏我首先選擇了jwood的 Standup Timer 項目。本文將把研究的內容筆記整理,創建一個索引列表。

關鍵詞

  Android.os.Handler涉及較多的知識點,我把一些關鍵詞列舉在下面,將主要介紹Handler:

android.os.Handler

  Handler在android裏負責發送和處理消息。它的主要用途有:
  1)按計劃發送消息或執行某個Runnanble(使用POST方法);
  2)從其餘線程中發送來的消息放入消息隊列中,避免線程衝突(常見於更新UI線程)
   默認狀況下,Handler接受的是當前線程下的消息循環實例(使用 Handler( Looper looper)、 Handler( Looper looper, Handler.Callback callback)能夠指定線程),同時一個消息隊列能夠被當前線程中的多個對象進行分發、處理(在UI線程中,系統已經有一個Activity來處理了,你能夠再起若干個Handler來處理)。在實例化Handler的時候,Looper能夠是任意線程的,只要有Handler的指針,任何線程也均可以sendMessage。Handler對於Message的處理不是併發的。一個Looper 只有處理完一條Message纔會讀取下一條,因此消息的處理是阻塞形式的(handleMessage()方法裏不該該有耗時操做,能夠將耗時操做放在其餘線程執行,操做完後發送Message(經過sendMessges方法),而後由handleMessage()更新UI)。

倒計時程序

  利用Timer 編寫一個倒計時程序,程序使用Timer和TimerTask來完成倒計時,同時使用sendMessages方法發送消息,而後在HanleMessage裏更新UI。
Activity佈局:

<?
xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_gravity="center" android:id="@+id/txt" /> <Button android:id="@+id/btnStartTime" android:text="開始計時" android:layout_width="80dip" android:layout_height="wrap_content" ></Button> <Button android:id="@+id/btnStopTime" android:text="中止計時" android:layout_width="80dip" android:layout_height="wrap_content" /> <SeekBar android:id="@+id/SeekBar01" android:layout_width="match_parent" android:layout_height="wrap_content"></SeekBar> </LinearLayout>

 

這裏使用TextView 來顯示倒計時的時間變化,兩個按鈕用於控制時間的開始和中止。SeekBar主要是用於查看線程是否被阻塞(阻塞時沒法拖動)。
 

  @Override publicvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); setContentView(R.layout.main); txt = (TextView) findViewById(R.id.txt); btnStart = (Button) findViewById(R.id.btnStartTime); btnStop = (Button) findViewById(R.id.btnStopTime); Log.d("ThreadId", "onCread:" + String.valueOf(Thread.currentThread().getId())); myHandler =new Handler(this); btnStart.setOnClickListener(this); btnStop.setOnClickListener(this); }

 

在onCreate方法中初始化元素個元素,myHandler = new Handler(this); 調用的是  Handler( Handler.Callback callback)構造函數,在回調方法callback中對發送來的消息進行處理(這樣咱們就沒必要使用內部類的寫法來 重寫HandleMessage()方法了),所以Activity必須實現 android.os.Handler.Callback 接口。咱們還在將onCreate 方法的ThreadId 記錄在了Log中用以和消息發送、處理時所做的線程進行比較。
 
發送消息


    @Override
    publicvoid onClick(View v) {
        switch (v.getId()) {
        case R.id.btnStartTime:
            startTimer();
            break;
        case R.id.btnStopTime:
            timer.cancel();

            break;
        }

    }

    privatesynchronizedvoid startTimer() {

        timer =new Timer();
        // TimerTask updateTimerValuesTask = new TimerTask() {
        // @Override
        // public void run() {
        // updateTimerValues();
        // }
        //
        // };
        //自定義的CallBack模式。Task繼承自TimerTask
        Task updateTimerValuesTask =new Task(this);

        timer.schedule(updateTimerValuesTask, 1000, 1000);
    }

    //執行耗時的倒計時任務。
privatevoid updateTimerValues() {
        total--;

        Log.d("ThreadId", "send:"
                + String.valueOf(Thread.currentThread().getId()));
        
        Message msg=new Message();
        Bundle date =new Bundle();// 存放數據
        date.putInt("time", total);
        msg.setData(date);
        msg.what=0;
        myHandler.sendMessage(msg);

        //另外一種寫法
//        Message msg=myHandler.obtainMessage();
//        Bundle date = new Bundle();// 存放數據
//        date.putInt("time", total);
//        msg.setData(date);
//        msg.what=0;
//        msg.sendToTarget();
        
    }

    @Override
    publicvoid TaskRun() {
        updateTimerValues();

    }

 

 

實現Button按鈕的事件處理以此進入倒計時操做。這裏使用的Timer 來執行定時操做(其實咱們徹底能夠另起一個線程)。Task類繼承了TimerTask類,裏面增長了一個任務處理接口來實現回調模式,應此Activity須要實現該回調的接口 ITaskCallBack(這樣作是由於我比較不喜歡內部類的編寫方法)。
ICallBack接口和Task類

publicinterface ITaskCallBack {

    void TaskRun();
}



publicclass Task extends TimerTask {

    private ITaskCallBack iTask;
    
    public Task(ITaskCallBack iTaskCallBack)
    {
        super();
        iTask=iTaskCallBack;
    }
    
    publicvoid setCallBack(ITaskCallBack iTaskCallBack)
    {
        iTask=iTaskCallBack;
    }
    @Override
    publicvoid run() {
        // TODO Auto-generated method stub
        iTask.TaskRun();
    }

}

 

 

這是Java的回調函數的通常寫法。
 
實現CallBack


    /**
     * 實現消息處理
     */
    @Override
    publicboolean handleMessage(Message msg) {
    
        switch(msg.what)
        {
        case0:
            Bundle date=msg.getData();
            txt.setText(String.valueOf(date.getInt("time")));
            
            Log.d("ThreadId", "HandlerMessage:"
                    + String.valueOf(Thread.currentThread().getId()));
            Log.d("ThreadId", "msgDate:"
                    + String.valueOf(date.getInt("time")));
            break;

        }
        returnfalse;
    }

 

 

  能夠看到 實現 android.os.Handler.Callback 接口,其實就是對handleMessage()方法進行重寫(和內部類的一個區別是,內部類的返回值是Void)。

運行結果

  能夠看到在onCreate 方法中線程的ID是1(UI線程) 這與 HandlerMessage 進行消息處理時是所做的線程ID是同樣的,而消息發送的線程ID則爲8非UI線程。

使用Threadle進行實現

Activity類


publicclass ThreadHandlerrActivity extends Activity implements Callback,
        OnClickListener {

    private TextView txt;
    private Button btnStart, btnStop;
    private Handler myHandler;
    private TimerThread timerThread;
    privateint Total=30;


    /** Called when the activity is first created. */
    @Override
    publicvoid onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        txt = (TextView) findViewById(R.id.txt);
        btnStart = (Button) findViewById(R.id.btnStartTime);
        btnStop = (Button) findViewById(R.id.btnStopTime);
        Log.d("ThreadId", "onCread:"
                + String.valueOf(Thread.currentThread().getId()));
        myHandler =new Handler(this);
        
    
        btnStart.setOnClickListener(this);
        btnStop.setOnClickListener(this);

    }

    /**
     * 實現消息處理
     */
    @Override
    publicboolean handleMessage(Message msg) {
    
        switch(msg.what)
        {
        case0:
            Bundle date=msg.getData();
            txt.setText(String.valueOf(date.getInt("time")));
            
            Log.d("ThreadId", "HandlerMessage:"
                    + String.valueOf(Thread.currentThread().getId()));
            Log.d("ThreadId", "msgDate:"
                    + String.valueOf(date.getInt("time")));
            break;

        }
        returnfalse;
    }

    @Override
    publicvoid onClick(View v) {
        switch (v.getId()) {
        case R.id.btnStartTime:
            //自定義的線程
        timerThread=new TimerThread(myHandler,60);
            timerThread.start();
            
            break;
        case R.id.btnStopTime:
            timerThread.stop();
            //timerThread.destroy();
break;
        }

    }

    
        
    
}

 

自定義的線程類

**
 * 自定義的線程類,經過傳入的Handler,和Total 按期執行耗時操做
 * @author linzijun
 *
 */
publicclass TimerThread extends Thread  {

    publicint Total=60;
    public Handler handler;
    /**
     * 初始化構造函數
     * @param mhandler handler 用於發送消息
     * @param total 總週期
     */
    public TimerThread(Handler mhandler,int total)
    {
        super();
        handler=mhandler;
        Total=total;
    }
    @Override
    publicvoid run() {
        
        while(true)
        {
            Total--;
            if(Total<0)
                break;
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            Message msg=new Message();
            Bundle date =new Bundle();// 存放數據
            date.putInt("time", Total);
            msg.setData(date);
            msg.what=0;
            Log.d("ThreadId", "Thread:"
                    + String.valueOf(Thread.currentThread().getId()));
            handler.sendMessage(msg);
            
            
        }
        
        super.run();
    }

    
    
}

 

這裏繼承了Thread類,也能夠直接實現 Runnable接口。

關於POST

  Post的各類方法是把一個Runnable發送給消息隊列,它將在到達時進行處理。
POST


publicclass PostHandler extends Activity implements OnClickListener, Runnable {

    private TextView txt;
    private Button btnStart, btnStop;
    private Handler myHandler;
    private Timer timer;
    privateint total =60;

    
    @Override
    protectedvoid onCreate(Bundle savedInstanceState) {
        // TODO Auto-generated method stub
super.onCreate(savedInstanceState);

        setContentView(R.layout.main);
        txt = (TextView) findViewById(R.id.txt);
        btnStart = (Button) findViewById(R.id.btnStartTime);
        btnStop = (Button) findViewById(R.id.btnStopTime);
        Log.d("ThreadId", "onCread:"
                + String.valueOf(Thread.currentThread().getId()));
        myHandler =new Handler()
        {

            @Override
            publicvoid handleMessage(Message msg) {
                switch(msg.what)
                {
                case0:
                    Bundle date=msg.getData();
                    txt.setText(String.valueOf(date.getInt("time")));
                    
                    Log.d("ThreadId", "HandlerMessage:"
                            + String.valueOf(Thread.currentThread().getId()));
                    Log.d("ThreadId", "msgDate:"
                            + String.valueOf(date.getInt("time")));
                    break;

                }
    
            }
            
        };

        btnStart.setOnClickListener(this);
        btnStop.setOnClickListener(this);
    }

    @Override
    publicvoid onClick(View v) {
        switch (v.getId()) {
        case R.id.btnStartTime:
            //myHandler.post(this);
            myHandler.postDelayed(this, 1000);
            break;
        case R.id.btnStopTime:
            
            break;
        }
        
    }

    @Override
    publicvoid run() {
        while(true)
        {
            total--;
            if(total<0)
                break;
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            Message msg=new Message();
            Bundle date =new Bundle();// 存放數據
            date.putInt("time", total);
            msg.setData(date);
            msg.what=0;
            Log.d("ThreadId", "POST:"
                    + String.valueOf(Thread.currentThread().getId()));
            myHandler.sendMessage(msg);
            Log.d("ThreadId", "Thread:"
                    + String.valueOf(Thread.currentThread().getId()));

        }
        
    }

}

 

使用POST的方式 是將Runnable 一塊兒發送給處理的線程(這裏爲UI),若是Runnable的操做比較耗時的話那線程將進入阻塞狀態。能夠看到先運行 Runnable的Run方法 而後在進入 HandleMessage() 。我還嘗試了另外一種寫法,將TimerThreadPOST過去,運行結果是同樣的。
POST


publicclass PostHandler extends Activity implements OnClickListener, Runnable {

    private TextView txt;
    private Button btnStart, btnStop;
    private Handler myHandler;
    private Timer timer;
    privateint total =60;

    
    @Override
    protectedvoid onCreate(Bundle savedInstanceState) {
        // TODO Auto-generated method stub
super.onCreate(savedInstanceState);

        setContentView(R.layout.main);
        txt = (TextView) findViewById(R.id.txt);
        btnStart = (Button) findViewById(R.id.btnStartTime);
        btnStop = (Button) findViewById(R.id.btnStopTime);
        Log.d("ThreadId", "onCread:"
                + String.valueOf(Thread.currentThread().getId()));
        myHandler =new Handler()
        {

            @Override
            publicvoid handleMessage(Message msg) {
                switch(msg.what)
                {
                case0:
                    Bundle date=msg.getData();
                    txt.setText(String.valueOf(date.getInt("time")));
                    
                    Log.d("ThreadId", "HandlerMessage:"
                            + String.valueOf(Thread.currentThread().getId()));
                    Log.d("ThreadId", "msgDate:"
                            + String.valueOf(date.getInt("time")));
                    break;

                }
    
            }
            
        };

        btnStart.setOnClickListener(this);
        btnStop.setOnClickListener(this);
    }

    @Override
    publicvoid onClick(View v) {
        switch (v.getId()) {
        case R.id.btnStartTime:
            //myHandler.post(this);
            myHandler.postDelayed(this, 1000);
            break;
        case R.id.btnStopTime:
            
            break;
        }
        
    }

    @Override
    publicvoid run() {
        while(true)
        {
            total--;
            if(total<0)
                break;
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            Message msg=new Message();
            Bundle date =new Bundle();// 存放數據
            date.putInt("time", total);
            msg.setData(date);
            msg.what=0;
            Log.d("ThreadId", "POST:"
                    + String.valueOf(Thread.currentThread().getId()));
            myHandler.sendMessage(msg);
            Log.d("ThreadId", "Thread:"
                    + String.valueOf(Thread.currentThread().getId()));

        }
        
    }

}

 

 

能夠說POST的各類方法主要是用於 「按計劃發送消息或執行某個Runnanble(使用POST方法)」。

參考文獻

   SDK
相關文章
相關標籤/搜索