Android实战技巧:Handler

原文地址:http://blog.csdn.net/hitlion2008/article/details/7562803

Handler之消息循环

Handler是用于操作线程内部的消息队列的类。这有点绕,没关系,我们慢慢的来讲。前面Looper一篇讲到了Looper是用于给线程创建消息队列用的,也就是说Looper可以让消息队列(MessageQueue)附属在线程之内,并让消息队列循环起来,接收并处理消息。但,我们并不直接的操作消息队列,而是用Handler来操作消息队列,给消息队列发送消息,和从消息队列中取出消息并处理。这就是Handler的职责。
Handler,Looper和MessageQueue是属于一个线程内部的数据,但是它提供给外部线程访问的接口,Handler就是公开给外部线程,与线程通讯的接口。换句话说,这三个东西都是用来线程间通讯用的(ITC--Inter Thread Communication),与进行间通讯(IPC--Inter Process Communication)的消息队列msgque的核心思想是一致的。MessageQueue是相对较底层的,较少直接使用,Looper和Handler就是专门用来操作底层MessageQueue的。
还有一个重要的数据结构是通讯的基本元素,就是消息对象(Message),Message从来不单独使用,它都是跟随Handler来使用的。具体方法可以参考文档,但需要注意的是同一个消息对象不能发送二次,否则会有AndroidRuntimeException: { what=1000 when=-15ms obj=.. } This message is already in use."。每次发送消息前都要通过Message.obtain()来获取新的对象,或者,对于不需要传送额外数据的直接发送空消息就好Handler.sendEmptyMessage(int)。另外也需要注意消息对象是不能手动回收的,也就是说你不能调用Message.recycle()来释放一个消息对象,因为当该对象被从队列中取出处理完毕后,MessageQueue内部会自动的去做recycle()。这个理解起来也很容易,因为发送一个消息到消息队列后,消息什么时候会被处理,对于应用程序来讲是不知道的,只有MessageQueue才会知道,所以只能由MessageQueue来做回收释放的动作。
因为Handler是用于操作一个线程内部的消息队列的,所以Handler必须依附于一个线程,而且只能是一个线程。换句话说,你必须在一个线程内创建Handler,同时指定Handler的回调handlerMessage(Message msg)。
Handler主要有二个用途,一个是用于线程内部消息循环; 另外一个就是用于线程间通讯。
Handler的基本用法可以参考文档,说的还是比较清楚的。

用于线程内部消息循环

主要是用作在将来定时做某个动作,或者循环性,周期性的做某个动作。主要的接口就是
  • Handler.sendEmptyMessageDelayed(int msgid, long after);
  • Handler.sendMessageDelayed(Message msg, long after);
  • Handler.postDelayed(Runnable task, long after);
  • Handler.sendMessageAtTime(Message msg, long timeMillis);
  • Handler.sendEmptyMessageAtTime(int id, long timeMiilis);
  • Handler.postAtTime(Runnable task, long timeMillis);
这些方法的目的都是设置一个定时器,在指定的时间后,或者在指定的时间向Handler所在的MessageQueue发送消息。这样就非常方便应用程序实现定时操作,或者循环时序操作(处理消息时再延时发送消息,以达成循环时序)。

这个使用起来并不难,但需要注意一点的是,线程内部消息循环并不是并发处理,也就是所有的消息都是在Handler所属的线程内处理的,所以虽然你用post(Runnable r),发给MessageQueue一个Runnable,但这并不会创建新的线程来执行,处理此消息时仅是调用r.run()。(想要另起线程执行,必须把Runnable放到一个Thread中)。

实例

这里用一个实例来展示主线程通过Handler与后台线程进行通信,并且主线程用Handler来实现循环时序。


播放一个视频,线程用于创建和初始化MediaPlayer,初始化好后会通过主线程的Handler告诉主线程,然后主线程可以播放视频,在播放过程中通过sendMessageDelayed()来实现播放进度的不断更新:

  1. public class HandlerSimpleDemo extends Activity {  
  2.     protected static final String TAG = "HandlerSimpleDemo";  
  3.     private static final int MEDIA_PLAYER_READY = 0;  
  4.     private static final int REFRESH_PROGRESS = 1;  
  5.       
  6.     private Button mStart;  
  7.     private Button mStop;  
  8.     private SurfaceHolder mSurfaceHolder;  
  9.     private ProgressBar mProgressBar;  
  10.     private SurfaceView mDisplay;  
  11.     private MediaPlayer mMediaPlayer;  
  12.       
  13.     private Handler mMainHandler = new Handler() {  
  14.     @Override  
  15.     public void handleMessage(Message msg) {  
  16.         switch (msg.what) {  
  17.         case MEDIA_PLAYER_READY:  
  18.         mProgressBar.setMax(mMediaPlayer.getDuration());  
  19.         mMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {  
  20.             public void onCompletion(MediaPlayer mp) {  
  21.             mProgressBar.setProgress(mMediaPlayer.getDuration());  
  22.             mMainHandler.removeMessages(REFRESH_PROGRESS);  
  23.             }  
  24.         });  
  25.         mStart.setEnabled(true);  
  26.         mStop.setEnabled(true);  
  27.         break;  
  28.         case REFRESH_PROGRESS:  
  29.         int cp = mMediaPlayer.getCurrentPosition();  
  30.         mProgressBar.setProgress(cp);  
  31.         int delay = 1000 - (cp % 1000);  
  32.         mMainHandler.sendEmptyMessageDelayed(REFRESH_PROGRESS, delay);  
  33.         break;  
  34.         default:  
  35.         break;  
  36.         }  
  37.     }  
  38.     };  
  39.       
  40.     @SuppressWarnings("deprecation")  
  41.     @Override  
  42.     protected void onCreate(Bundle savedInstanceState) {  
  43.     super.onCreate(savedInstanceState);  
  44.     setContentView(R.layout.handler_simple_demo);  
  45.     mStart = (Button) findViewById(R.id.handler_simple_start);  
  46.     mStart.setOnClickListener(new View.OnClickListener() {  
  47.         public void onClick(View v) {  
  48.         mMediaPlayer.start();  
  49.         mMainHandler.sendEmptyMessage(REFRESH_PROGRESS);  
  50.         }  
  51.     });  
  52.     mStart.setEnabled(false);  
  53.     mStop = (Button) findViewById(R.id.handler_simple_stop);  
  54.     mStop.setOnClickListener(new View.OnClickListener() {  
  55.         public void onClick(View v) {  
  56.         mMainHandler.removeMessages(REFRESH_PROGRESS);  
  57.         mMediaPlayer.pause();  
  58.         }  
  59.     });  
  60.     mStop.setEnabled(false);  
  61.     mProgressBar = (ProgressBar) findViewById(R.id.handler_simple_progress);  
  62.     mDisplay = (SurfaceView) findViewById(R.id.handler_simple_display);  
  63.     mSurfaceHolder = mDisplay.getHolder();  
  64.     mSurfaceHolder.setFixedSize(mDisplay.getWidth(), mDisplay.getHeight());  
  65.     // Do not believe the document, setType is necessary, otherwise, video won't play correctly  
  66.     mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);  
  67.       
  68.     new Thread(new Runnable() {  
  69.         public void run() {  
  70.         try {  
  71.             mMediaPlayer = MediaPlayer.create(getApplication(), R.raw.flug);  
  72.             mMediaPlayer.setDisplay(mSurfaceHolder);  
  73.             mMainHandler.sendEmptyMessage(MEDIA_PLAYER_READY);  
  74.         } catch (IllegalArgumentException e) {  
  75.             Log.e(TAG, "caught exception e", e);  
  76.         } catch (SecurityException e) {  
  77.             Log.e(TAG, "caught exception e", e);  
  78.         } catch (IllegalStateException e) {  
  79.             Log.e(TAG, "caught exception e", e);  
  80.         }  
  81.         }  
  82.     }).start();  
  83.     }  
  84.   
  85.     @Override  
  86.     protected void onDestroy() {  
  87.     super.onDestroy();  
  88.     mMainHandler.removeMessages(REFRESH_PROGRESS);  
  89.     if (mMediaPlayer != null) {  
  90.         mMediaPlayer.release();  
  91.     }  
  92.     }  


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值