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佈局:
Layout
複製代碼
<? 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主要是用於查看線程是否被阻塞(阻塞時沒法拖動)。
onCreate
複製代碼
@Override
public void 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
public void onClick(View v) {
switch (v.getId()) {
case R.id.btnStartTime:
            startTimer();
break ;
case R.id.btnStopTime:
            timer.cancel();

break ;
        }

    }

private synchronized void 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 );
    }

// 執行耗時的倒計時任務。
private void 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
public void TaskRun() {
        updateTimerValues();

    }

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

void TaskRun();
}



public class Task extends TimerTask {

private ITaskCallBack iTask;

public Task(ITaskCallBack iTaskCallBack)
    {
super ();
        iTask
= iTaskCallBack;
    }

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

}
複製代碼
這是Java的回調函數的通常寫法。
實現CallBack
複製代碼

/**
     * 實現消息處理
*/
    @Override
public boolean handleMessage(Message msg) {

switch (msg.what)
        {
case 0 :
            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 ;

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

運行結果

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

使用Threadle進行實現

Activity類
複製代碼

public class ThreadHandlerrActivity extends Activity implements Callback,
        OnClickListener {

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


/** Called when the activity is first created. */
    @Override
public void 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
public boolean handleMessage(Message msg) {

switch (msg.what)
        {
case 0 :
            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 ;

        }
return false ;
    }

    @Override
public void 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
*
*/
public class TimerThread extends Thread  {

public int Total = 60 ;
public Handler handler;
/**
     * 初始化構造函數
*
@param mhandler handler 用於發送消息
*
@param total 總週期
*/
public TimerThread(Handler mhandler, int total)
    {
super ();
        handler
= mhandler;
        Total
= total;
    }
    @Override
public void 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
複製代碼

public class PostHandler extends Activity implements OnClickListener, Runnable {

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


    @Override
protected void 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
public void handleMessage(Message msg) {
switch (msg.what)
                {
case 0 :
                    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
public void onClick(View v) {
switch (v.getId()) {
case R.id.btnStartTime:
// myHandler.post(this);
            myHandler.postDelayed( this , 1000 );
break ;
case R.id.btnStopTime:

break ;
        }

    }

    @Override
public void 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過去,運行結果是同樣的。
代碼
複製代碼
package zijunlin.me;

import java.util.Timer;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;

public class PostHandler extends Activity implements OnClickListener, Runnable {

private TextView txt;
private Button btnStart, btnStop;
private Handler myHandler;
private Timer timer;
private int total = 60 ;
private TimerThread timerThread;

    @Override
protected void 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
public void handleMessage(Message msg) {
switch (msg.what)
                {
case 0 :
                    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
public void onClick(View v) {
switch (v.getId()) {
case R.id.btnStartTime:
// myHandler.post(this);
// myHandler.postDelayed(this, 1000);
timerThread = new TimerThread(myHandler, 60 );

            myHandler.post(timerThread);
break ;
case R.id.btnStopTime:

break ;
        }

    }

    @Override
public void 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()));

        }

    }

}
 
請你們尊重原創,本人的轉帖主要是爲了我的學習
相關文章
相關標籤/搜索