欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android Handler之消息循环的深入解析

程序员文章站 2023-11-20 17:36:10
handler是用于操作线程内部的消息队列的类。这有点绕,没关系,我们慢慢的来讲。前面looper一篇讲到了looper是用于给线程创建消息队列用的,也就是说looper可...
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来实现循环时序。
Android Handler之消息循环的深入解析Android Handler之消息循环的深入解析
播放一个视频,线程用于创建和初始化mediaplayer,初始化好后会通过主线程的handler告诉主线程,然后主线程可以播放视频,在播放过程中通过sendmessagedelayed()来实现播放进度的不断更新:
复制代码 代码如下:

public class handlersimpledemo extends activity {
    protected static final string tag = "handlersimpledemo";
    private static final int media_player_ready = 0;
    private static final int refresh_progress = 1;

    private button mstart;
    private button mstop;
    private surfaceholder msurfaceholder;
    private progressbar mprogressbar;
    private surfaceview mdisplay;
    private mediaplayer mmediaplayer;

    private handler mmainhandler = new handler() {
 @override
 public void handlemessage(message msg) {
     switch (msg.what) {
     case media_player_ready:
  mprogressbar.setmax(mmediaplayer.getduration());
  mmediaplayer.setoncompletionlistener(new mediaplayer.oncompletionlistener() {
      public void oncompletion(mediaplayer mp) {
   mprogressbar.setprogress(mmediaplayer.getduration());
   mmainhandler.removemessages(refresh_progress);
      }
  });
  mstart.setenabled(true);
  mstop.setenabled(true);
  break;
     case refresh_progress:
  int cp = mmediaplayer.getcurrentposition();
  mprogressbar.setprogress(cp);
  int delay = 1000 - (cp % 1000);
  mmainhandler.sendemptymessagedelayed(refresh_progress, delay);
  break;
     default:
  break;
     }
 }
    };

    @suppresswarnings("deprecation")
    @override
    protected void oncreate(bundle savedinstancestate) {
 super.oncreate(savedinstancestate);
 setcontentview(r.layout.handler_simple_demo);
 mstart = (button) findviewbyid(r.id.handler_simple_start);
 mstart.setonclicklistener(new view.onclicklistener() {
     public void onclick(view v) {
  mmediaplayer.start();
  mmainhandler.sendemptymessage(refresh_progress);
     }
 });
 mstart.setenabled(false);
 mstop = (button) findviewbyid(r.id.handler_simple_stop);
 mstop.setonclicklistener(new view.onclicklistener() {
     public void onclick(view v) {
  mmainhandler.removemessages(refresh_progress);
  mmediaplayer.pause();
     }
 });
 mstop.setenabled(false);
 mprogressbar = (progressbar) findviewbyid(r.id.handler_simple_progress);
 mdisplay = (surfaceview) findviewbyid(r.id.handler_simple_display);
 msurfaceholder = mdisplay.getholder();
 msurfaceholder.setfixedsize(mdisplay.getwidth(), mdisplay.getheight());
 // do not believe the document, settype is necessary, otherwise, video won't play correctly
 msurfaceholder.settype(surfaceholder.surface_type_push_buffers);

 new thread(new runnable() {
     public void run() {
  try {
      mmediaplayer = mediaplayer.create(getapplication(), r.raw.flug);
      mmediaplayer.setdisplay(msurfaceholder);
      mmainhandler.sendemptymessage(media_player_ready);
  } catch (illegalargumentexception e) {
      log.e(tag, "caught exception e", e);
  } catch (securityexception e) {
      log.e(tag, "caught exception e", e);
  } catch (illegalstateexception e) {
      log.e(tag, "caught exception e", e);
  }
     }
 }).start();
    }
    @override
    protected void ondestroy() {
 super.ondestroy();
 mmainhandler.removemessages(refresh_progress);
 if (mmediaplayer != null) {
     mmediaplayer.release();
 }
    }
}