android中handler案例

来源:互联网 发布:全国居民小区数据库 编辑:程序博客网 时间:2024/04/29 01:12

前言
  学习android一段时间了,为了进一步了解android的应用是如何设计开发的,决定详细研究几个开源的android应用。从一些开源应用中吸收点东西,一边进行量的积累,一边探索android的学习研究方向。这里我首先选择了jwood的 Standup Timer 项目。本文将把研究的内容笔记整理,建立一个索引列表。
关键词
  Android.os.Handler涉及较多的知识点,我把一些关键词列举在下面,将主要介绍Handler:
android.os.Handler 、 android.os.Handler.Callback 
Looper、 
Threadle、Runnable 
Message、Message queue 
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
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3.     android:orientation="vertical"
  4.     android:layout_width="fill_parent"
  5.     android:layout_height="fill_parent"
  6.     >
  7. <TextView  
  8.     android:layout_width="fill_parent" 
  9.     android:layout_height="wrap_content" 
  10.     android:layout_gravity="center"
  11.     android:id="@+id/txt"
  12.     />
  13. <Button
  14.     android:id="@+id/btnStartTime"
  15.     android:text="开始计时"
  16.     android:layout_width="80dip"
  17.     android:layout_height="wrap_content" 

  18.     ></Button>
  19. <Button
  20.      android:id="@+id/btnStopTime"
  21.      android:text="停止计时"
  22.      android:layout_width="80dip"
  23.     android:layout_height="wrap_content"
  24.   />
  25.   
  26. <SeekBar android:id="@+id/SeekBar01" android:layout_width="match_parent" android:layout_height="wrap_content"></SeekBar>
  27. </LinearLayout>
复制代码
这里使用TextView 来显示倒计时的时间变化,两个按钮用于控制时间的开始和停止。SeekBar主要是用于查看线程是否被阻塞(阻塞时无法拖动)。
  1. onCreate 
  2. @Override
  3.     public void onCreate(Bundle savedInstanceState) {
  4.         super.onCreate(savedInstanceState);
  5.         setContentView(R.layout.main);
  6.         txt = (TextView) findViewById(R.id.txt);
  7.         btnStart = (Button) findViewById(R.id.btnStartTime);
  8.         btnStop = (Button) findViewById(R.id.btnStopTime);
  9.         Log.d("ThreadId", "onCread:"
  10.                 + String.valueOf(Thread.currentThread().getId()));
  11.         myHandler = new Handler(this);

  12.         btnStart.setOnClickListener(this);
  13.         btnStop.setOnClickListener(this);

  14.     }
复制代码
在onCreate方法中初始化元素个元素,myHandler = new Handler(this); 调用的是  Handler(Handler.Callback callback)构造函数,在回调方法callback中对发送来的消息进行处理(这样我们就不必使用内部类的写法来 重写HandleMessage()方法了),因此Activity必须实现 android.os.Handler.Callback 接口。我们还在将onCreate 方法的ThreadId 记录在了Log中用以和消息发送、处理时所作的线程进行比较。
发送消息
  1.     @Override
  2.     public void onClick(View v) {
  3.         switch (v.getId()) {
  4.         case R.id.btnStartTime:
  5.             startTimer();
  6.             break;
  7.         case R.id.btnStopTime:
  8.             timer.cancel();

  9.             break;
  10.         }

  11.     }

  12.     private synchronized void startTimer() {

  13.         timer = new Timer();
  14.         // TimerTask updateTimerValuesTask = new TimerTask() {
  15.         // @Override
  16.         // public void run() {
  17.         // updateTimerValues();
  18.         // }
  19.         //
  20.         // };
  21.         //自定义的CallBack模式。Task继承自TimerTask
  22.         Task updateTimerValuesTask = new Task(this);

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

  25.     //执行耗时的倒计时任务。
  26.     private void updateTimerValues() {
  27.         total--;

  28.         Log.d("ThreadId", "send:"
  29.                 + String.valueOf(Thread.currentThread().getId()));
  30.         
  31.         Message msg=new Message();
  32.         Bundle date = new Bundle();// 存放数据
  33.         date.putInt("time", total);
  34.         msg.setData(date);
  35.         msg.what=0;
  36.         myHandler.sendMessage(msg);

  37.         //另一种写法
  38. //        Message msg=myHandler.obtainMessage();
  39. //        Bundle date = new Bundle();// 存放数据
  40. //        date.putInt("time", total);
  41. //        msg.setData(date);
  42. //        msg.what=0;
  43. //        msg.sendToTarget();
  44.         
  45.     }

  46.     @Override
  47.     public void TaskRun() {
  48.         updateTimerValues();

  49.     }
复制代码
实现Button按钮的事件处理以此进入倒计时操作。这里使用的Timer 来执行定时操作(其实我们完全可以另起一个线程)。Task类继承了TimerTask类,里面增加了一个任务处理接口来实现回调模式,应此Activity需要实现该回调的接口 ITaskCallBack(这样做是因为我比较不喜欢内部类的编写方法)。
ICallBack接口和Task类
  1. public interface ITaskCallBack {

  2.     void TaskRun();
  3. }



  4. public class Task extends TimerTask {

  5.     private ITaskCallBack iTask;
  6.     
  7.     public Task(ITaskCallBack iTaskCallBack)
  8.     {
  9.         super();
  10.         iTask=iTaskCallBack;
  11.     }
  12.     
  13.     public void setCallBack(ITaskCallBack iTaskCallBack)
  14.     {
  15.         iTask=iTaskCallBack;
  16.     }
  17.     @Override
  18.     public void run() {
  19.         // TODO Auto-generated method stub
  20.         iTask.TaskRun();
  21.     }

  22. }
复制代码
这是Java的回调函数的一般写法。
实现CallBack
  1.     /**
  2.      * 实现消息处理
  3.      */
  4.     @Override
  5.     public boolean handleMessage(Message msg) {
  6.     
  7.         switch(msg.what)
  8.         {
  9.         case 0:
  10.             Bundle date=msg.getData();
  11.             txt.setText(String.valueOf(date.getInt("time")));
  12.             
  13.             Log.d("ThreadId", "HandlerMessage:"
  14.                     + String.valueOf(Thread.currentThread().getId()));
  15.             Log.d("ThreadId", "msgDate:"
  16.                     + String.valueOf(date.getInt("time")));
  17.             break;

  18.         }
  19.         return false;
  20.     }
复制代码
  可以看到 实现 android.os.Handler.Callback 接口,其实就是对handleMessage()方法进行重写(和内部类的一个区别是,内部类的返回值是Void)。
运行结果

  可以看到在onCreate 方法中线程的ID是1(UI线程) 这与 HandlerMessage 进行消息处理时是所作的线程ID是一样的,而消息发送的线程ID则为8非UI线程。
使用Threadle进行实现
Activity类
  1. public class ThreadHandlerrActivity extends Activity implements Callback,
  2.         OnClickListener {

  3.     private TextView txt;
  4.     private Button btnStart, btnStop;
  5.     private Handler myHandler;
  6.     private TimerThread timerThread;
  7.     private int Total=30;


  8.     /** Called when the activity is first created. */
  9.     @Override
  10.     public void onCreate(Bundle savedInstanceState) {
  11.         super.onCreate(savedInstanceState);
  12.         setContentView(R.layout.main);
  13.         txt = (TextView) findViewById(R.id.txt);
  14.         btnStart = (Button) findViewById(R.id.btnStartTime);
  15.         btnStop = (Button) findViewById(R.id.btnStopTime);
  16.         Log.d("ThreadId", "onCread:"
  17.                 + String.valueOf(Thread.currentThread().getId()));
  18.         myHandler = new Handler(this);
  19.         
  20.     
  21.         btnStart.setOnClickListener(this);
  22.         btnStop.setOnClickListener(this);

  23.     }

  24.     /**
  25.      * 实现消息处理
  26.      */
  27.     @Override
  28.     public boolean handleMessage(Message msg) {
  29.     
  30.         switch(msg.what)
  31.         {
  32.         case 0:
  33.             Bundle date=msg.getData();
  34.             txt.setText(String.valueOf(date.getInt("time")));
  35.             
  36.             Log.d("ThreadId", "HandlerMessage:"
  37.                     + String.valueOf(Thread.currentThread().getId()));
  38.             Log.d("ThreadId", "msgDate:"
  39.                     + String.valueOf(date.getInt("time")));
  40.             break;

  41.         }
  42.         return false;
  43.     }

  44.     @Override
  45.     public void onClick(View v) {
  46.         switch (v.getId()) {
  47.         case R.id.btnStartTime:
  48.             //自定义的线程
  49.         timerThread=new TimerThread(myHandler,60);
  50.             timerThread.start();
  51.             
  52.             break;
  53.         case R.id.btnStopTime:
  54.             timerThread.stop();
  55.             //timerThread.destroy();
  56.             break;
  57.         }

  58.     }

  59.     
  60.         
  61.     
  62. }
复制代码
自定义的线程类
  1. **
  2. * 自定义的线程类,通过传入的Handler,和Total 定期执行耗时操作
  3. * @author linzijun
  4. *
  5. */
  6. public class TimerThread extends Thread  {

  7.     public int Total=60;
  8.     public Handler handler;
  9.     /**
  10.      * 初始化构造函数
  11.      * @param mhandler handler 用于发送消息
  12.      * @param total 总周期
  13.      */
  14.     public TimerThread(Handler mhandler,int total)
  15.     {
  16.         super();
  17.         handler=mhandler;
  18.         Total=total;
  19.     }
  20.     @Override
  21.     public void run() {
  22.         
  23.         while(true)
  24.         {
  25.             Total--;
  26.             if(Total<0)
  27.                 break;
  28.             try {
  29.                 Thread.sleep(1000);
  30.             } catch (InterruptedException e) {
  31.                 // TODO Auto-generated catch block
  32.                 e.printStackTrace();
  33.             }
  34.             Message msg=new Message();
  35.             Bundle date = new Bundle();// 存放数据
  36.             date.putInt("time", Total);
  37.             msg.setData(date);
  38.             msg.what=0;
  39.             Log.d("ThreadId", "Thread:"
  40.                     + String.valueOf(Thread.currentThread().getId()));
  41.             handler.sendMessage(msg);
  42.             
  43.             
  44.         }
  45.         
  46.         super.run();
  47.     }

  48.     
  49.     
  50. }
复制代码
这里继承了Thread类,也可以直接实现 Runnable接口。
关于POST
  Post的各种方法是把一个Runnable发送给消息队列,它将在到达时进行处理。
POST
  1. public class PostHandler extends Activity implements OnClickListener, Runnable {

  2.     private TextView txt;
  3.     private Button btnStart, btnStop;
  4.     private Handler myHandler;
  5.     private Timer timer;
  6.     private int total = 60;

  7.     
  8.     @Override
  9.     protected void onCreate(Bundle savedInstanceState) {
  10.         // TODO Auto-generated method stub
  11.         super.onCreate(savedInstanceState);

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

  20.             @Override
  21.             public void handleMessage(Message msg) {
  22.                 switch(msg.what)
  23.                 {
  24.                 case 0:
  25.                     Bundle date=msg.getData();
  26.                     txt.setText(String.valueOf(date.getInt("time")));
  27.                     
  28.                     Log.d("ThreadId", "HandlerMessage:"
  29.                             + String.valueOf(Thread.currentThread().getId()));
  30.                     Log.d("ThreadId", "msgDate:"
  31.                             + String.valueOf(date.getInt("time")));
  32.                     break;

  33.                 }
  34.     
  35.             }
  36.             
  37.         };

  38.         btnStart.setOnClickListener(this);
  39.         btnStop.setOnClickListener(this);
  40.     }

  41.     @Override
  42.     public void onClick(View v) {
  43.         switch (v.getId()) {
  44.         case R.id.btnStartTime:
  45.             //myHandler.post(this);
  46.             myHandler.postDelayed(this, 1000);
  47.             break;
  48.         case R.id.btnStopTime:
  49.             
  50.             break;
  51.         }
  52.         
  53.     }

  54.     @Override
  55.     public void run() {
  56.         while(true)
  57.         {
  58.             total--;
  59.             if(total<0)
  60.                 break;
  61.             try {
  62.                 Thread.sleep(1000);
  63.             } catch (InterruptedException e) {
  64.                 // TODO Auto-generated catch block
  65.                 e.printStackTrace();
  66.             }
  67.             Message msg=new Message();
  68.             Bundle date = new Bundle();// 存放数据
  69.             date.putInt("time", total);
  70.             msg.setData(date);
  71.             msg.what=0;
  72.             Log.d("ThreadId", "POST:"
  73.                     + String.valueOf(Thread.currentThread().getId()));
  74.             myHandler.sendMessage(msg);
  75.             Log.d("ThreadId", "Thread:"
  76.                     + String.valueOf(Thread.currentThread().getId()));

  77.         }
  78.         
  79.     }

  80. }
复制代码
使用POST的方式 是将Runnable 一起发送给处理的线程(这里为UI),如果Runnable的操作比较耗时的话那线程将进入阻塞状态。可以看到先运行 Runnable的Run方法 然后在进入 HandleMessage() 。我还尝试了另一种写法,将TimerThreadPOST过去,运行结果是一样的。
代码
  1. package zijunlin.me;

  2. import java.util.Timer;

  3. import android.app.Activity;
  4. import android.os.Bundle;
  5. import android.os.Handler;
  6. import android.os.Message;
  7. import android.util.Log;
  8. import android.view.View;
  9. import android.view.View.OnClickListener;
  10. import android.widget.Button;
  11. import android.widget.TextView;

  12. public class PostHandler extends Activity implements OnClickListener, Runnable {

  13.     private TextView txt;
  14.     private Button btnStart, btnStop;
  15.     private Handler myHandler;
  16.     private Timer timer;
  17.     private int total = 60;
  18.     private TimerThread timerThread;
  19.     
  20.     @Override
  21.     protected void onCreate(Bundle savedInstanceState) {
  22.         // TODO Auto-generated method stub
  23.         super.onCreate(savedInstanceState);

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

  32.             @Override
  33.             public void handleMessage(Message msg) {
  34.                 switch(msg.what)
  35.                 {
  36.                 case 0:
  37.                     Bundle date=msg.getData();
  38.                     txt.setText(String.valueOf(date.getInt("time")));
  39.                     
  40.                     Log.d("ThreadId", "HandlerMessage:"
  41.                             + String.valueOf(Thread.currentThread().getId()));
  42.                     Log.d("ThreadId", "msgDate:"
  43.                             + String.valueOf(date.getInt("time")));
  44.                     break;

  45.                 }
  46.     
  47.             }
  48.             
  49.         };

  50.         btnStart.setOnClickListener(this);
  51.         btnStop.setOnClickListener(this);
  52.     }

  53.     @Override
  54.     public void onClick(View v) {
  55.         switch (v.getId()) {
  56.         case R.id.btnStartTime:
  57.             //myHandler.post(this);
  58.             //myHandler.postDelayed(this, 1000);
  59.             timerThread=new TimerThread(myHandler,60);
  60.             
  61.             myHandler.post(timerThread);
  62.             break;
  63.         case R.id.btnStopTime:
  64.             
  65.             break;
  66.         }
  67.         
  68.     }

  69.     @Override
  70.     public void run() {
  71.         while(true)
  72.         {
  73.             total--;
  74.             if(total<0)
  75.                 break;
  76.             try {
  77.                 Thread.sleep(1000);
  78.             } catch (InterruptedException e) {
  79.                 // TODO Auto-generated catch block
  80.                 e.printStackTrace();
  81.             }
  82.             Message msg=new Message();
  83.             Bundle date = new Bundle();// 存放数据
  84.             date.putInt("time", total);
  85.             msg.setData(date);
  86.             msg.what=0;
  87.             Log.d("ThreadId", "POST:"
  88.                     + String.valueOf(Thread.currentThread().getId()));
  89.             myHandler.sendMessage(msg);
  90.             Log.d("ThreadId", "Thread:"
  91.                     + String.valueOf(Thread.currentThread().getId()));

  92.         }
  93.         
  94.     }

  95. }
复制代码

可以说POST的各种方法主要是用于 “按计划发送消息或执行某个Runnanble(使用POST方法)”。


文章原文http://www.eoeandroid.com/thread-116154-1-1.html