Android Handler和HandlerThread

Posted xhmj12

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android Handler和HandlerThread相关的知识,希望对你有一定的参考价值。

        

        Handler的官方解释如下:

        A Handler allows you to send and process Message and Runnable objects associated with a thread's MessageQueue.Each Handler instance is associated with a single thread and that thread's message queue.

        Handler会关联一个单独的线程和消息队列。Handler默认关联主线程,虽然要提供Runnable参数,但默认是直接调用Runnable的run()方法。也就是默认下会在主线程执行,如果在这里面的操作会有阻塞,界面也会卡住。如果要在其他线程执行,可以使用HandlerThread。

        Handler的使用方法:

        

Handler handler = new Handler()

        @Override
        public void handleMessage(Message msg)

                 super.handleMessage(msg); 
                 //处理发送过来的消息
                 Bundle b = msg.getData();
                System.out.println("msg:" + msg.arg1);                              
                System.out.println("msg:" + b.getString("name") + " - age:" + b.getInt("age")); 
        

;

        HandlerThread使用方法:
//把上面创建Handler的代码
  Handler handler = new Handler() 
      ...
     

 //改为:  
   HandlerThread thread = new HandlerThread("athread");     
   thread.start(); //要把线程启动   
   Handler handler = new Handler(thread.getLooper()) 
       ... 
     
 
 下面详细讲解下Handler的消息机制
 1.相关类:
 Message:消息,其中包含了消息ID,消息处理对象以及处理的数据等,由MessageQueue统一队列,终由Handler处理。
 Handler:处理者,负责Message的发送和处理,使用Hander时,需要实现handleMessage(Message msg)方法来对特定的Message进行处理,例如更新UI等。
 MessageQueue:消息队列,用来存放Handler发送过来的消息,并按照FIFO规则执行。
 当然,存放Message并非实际意义的保存,而是将Message以链表的方式串联起来的,等待Looper的抽取。
 Looper:消息泵,不断地从MessageQueue中抽取Message执行。因此,一个MessageQueue需要一个Looper。
 Thread:线程,负责调度整个消息循环,即消息循环的执行场所。
 
 Handler,Looper和MessageQueue就是简单的三角关系。Looper和MessageQueue一一对应,创建一个Looper的同时,会创建一个MessageQueue。而Handler与他们的关系,只是简单的聚集关系,即Handler里会引用当前线程里的特定Looper和MessageQueue。
 这样说来,多个Handler都可以共享同一Looper和MessageQueue了。当然,这些Handler也就运行在同一个线程里。
 
 接下来,我们简单的看下消息的循环过程:

 生成
  Message msg = mHandler.obtainMessage();        
  msg.what = what;       
  msg.sendToTarget(); 
 
 发送
   MessageQueue queue = mQueue;          
   if (queue != null)             
         msg.target = this;              
         sent = queue.enqueueMessage(msg, uptimeMillis);         
    
  在Handler.java的sendMessageAtTime(Message msg,long uptimeMillis)方法中,我们看到,它找到它所引用的MessageQueue,然后将Message的target设定成自己(目的是为了在处理消息环节,Message能找到正确的Handler),再将这个Message纳入到消息队列中。

 抽取
    Looper me = myLooper();          
    MessageQueue queue = me.mQueue;         
    while (true)               
        Message msg = queue.next(); // might block             
        if (msg != null)                   
            if (msg.target == null) 
               // No target is a magic identifier for the quit message.                               return;                
                              
            msg.target.dispatchMessage(msg);                 
            msg.recycle();             
                   
      
  
 在Looper.java的loop()函数里,我们看到,这里有一个死循环,不断地从MessageQueue中获取下一个(next())Message,然后通过Message中携带的target信息,交由正确的Handler处理(dispatchMessage方法)。

 处理在Looper.java的loop()函数里,我们看到,这里有一个死循环,不断地从MessageQueue中获取下一个(next())Message,然后通过Message中携带的target信息,交由正确的Handler处理(dispatchMessage方法)。

 处理
      if (msg.callback != null)              
          handleCallback(msg);         
        else               
           if (mCallback != null)                   
                if (mCallback.handleMessage(msg))                     
                     return;                
                              
                          
            handleMessage(msg);         
        
 
 在Handler.java的dispatchMessage(Message msg)方法里,其中的一个分支就是调用handleMessage方法来处理这条Message,而这也正是我们在职责处描述使用Handler时需要实现handleMessage(Message msg)的原因。
 至于dispatchMessage方法中的另外一个分支,将在后面的内容中说明。
 至此,我们看到,一个Message经由Handler的发送,MessageQueue的入队,Looper的抽取,又再一次地回到Handler的怀抱,而绕的这一圈,也正好帮助我们将同步操作变成了异步操作。
 
 下面,我们讨论下Handler所处的线程及更新UI的方式
 在主线程(UI线程)里,如果创建Handler时不传入Looper对象,那么将直接使用主线程(UI线程)的Looper对象(系统已经帮我们创建了);在其他线程里,如果创建Handler时不传入Looper对象,那么,这个Handler将不能接收处理消息。在这种情况下,通用的作法是:
 
        class LooperThread extends Thread                                 
              public Handler mHandler;                                
              public void run()                                                  
                   Looper.prepare();                                       
                   mHandler = new Handler()                                                              public void  handleMessage(Message msg)                                                        // process incoming messages here                                                                                                                           ;                                                 
                Looper.loop();                                
                          
         
 
 在创建Handler之前,为该线程准备好一个Looper(Looper.prepare),然后让这个Looper跑起来(Looper.loop),抽取Message,这样,Handler才能正常工作。
 因此,Handler处理消息总是在创建Handler的线程里运行。而消息处理中,不乏更新UI的操作,不正确的线程直接更新UI将引发异常。因此,需要时刻关心Handler在哪个线程里创建的。
 如何更新UI才能不出异常呢?SDK告诉我们,有以下4种方式可以从其他线程访问UI线程:
 1.Activity.runOnUiThread(Runnable)
 2.View.post(Runnable)
 3.View.postDelayed(Runnable,long)
 4.Handler
 其中,对于View.post(Runnable)方法。在post(Runnable action)方法里,View获取得到当前线程(即UI线程)的Handler,然后将action对象post到Handler里。在Handler里,它将传递过来的action对象包装成一个Message(Message的callback为action),然后将其投入UI线程的消息循环中。在Handler再次处理该Message时,有一条分支(未解释的那条)就是为它所设,直接调用runnable的run方法。而此时,已经路由到UI线程里,因此,我们可以毫无顾虑的更新UI。

 因此,注意如下几点:
 1.Handler的处理过程运行在创建Handler的线程里
 2.一个Looper对应一个MessageQueue
 3.一个线程对应一个Looper
 4.一个Looper可以对应多个Handler
 5.不确定当前线程时,更新UI时尽量调用post方法

    

         android提供的HandlerThread继承自Thread,并且在这个线程里放入了一个Looper(android.os.Looper -> java.lang.Object)对象,也就是说这个Looper对象是在新建线程里运转的。Looper对象可以简单的理解成一个循环处理工具。为了更好的理解HandlerThread,应该先看一下Handler相关的内容。

          从Android文档的Handler(android.os.Handler -> java.lang.Object)描述可以看出,Android的Handler和Java的Handler(java.util.logging.Handler -> java.lang.Object)是基本上没有关系的。Android提供的Handler是为了传递消息(android.os.Message)和启动Runnable对象而使用的。

android.os.Handler对象只能在某一个线程中运行并可以使用这个线程的消息队列(Message queue)。从官方API文档可以看到,实例化Handler有4种方法。其中有2种是需要Looper对象参数的,这2种构造方法就可以使Handler运行在包含给定Looper对象的线程中;如果不给定,则运行在当前线程中(一般没有新建线程的情况下,会运行在Android程序主线程中,也就UI线程中)。

          android.os.Handler在多线程和IPC(进程间交互)中都起到非常重要的做用。

android.os.Handler可以通过Looper对象实例化,并运行于另外的线程中,Android提供了让Handler运行于其它线程的线程实现,也是就HandlerThread。HandlerThread对象start后可以获得其Looper对象,并且使用这个Looper对象实例Handler,这样Handler就是工作于前面实例的HandlerThread(其实质就是线程Thread)里了。HandlerThread使用常用代码如下:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 void useHanlerThread()      // two ways to construct HandlerThread      HandlerThread hThread = new HandlerThread( "AnyName" ,              // a property in android.os.Process              Process.THREAD_PRIORITY_BACKGROUND);      // HandlerThread hThread2 = new HandlerThread("AnyName");      hThread.start();      // get Looper object of hThread(HandlerThread)      Looper looper = hThread.getLooper();      Handler h = new Handler(looper)          @Override          public void handleMessage(Message msg)              // TODO Auto-generated method stub              super.handleMessage(msg);               ;

HandlerThread是继承于Thread的,所以在Thread的适用范围内HandlerThread是同样适用的。如果在android.os.Handler里需要执行一些非常耗时的任务,就应该把Handler放到HandlerThread里面执行(或者在Handler里新开线程执行),而不应该直接在UI线程中执行,否则会增加出现ANR(Application Not Responding)的可能。

以上是关于Android Handler和HandlerThread的主要内容,如果未能解决你的问题,请参考以下文章

Android Handler机制

Android Handler机制

Android Handler面试总结

Android Handler面试总结

Android :安卓学习笔记之 Handler机制 的简单理解和使用

Android Handler和HandlerThread