zoukankan      html  css  js  c++  java
  • Android开发 之 理解Handler、Looper、MessageQueue、Thread关系

     

    本文转自博客:http://blog.csdn.net/he90227/article/details/43567073

     

    一. 图解与概述

     

    首先Android中 的每一个线程都会对应一个MessageQueue和Looper。见名知意,MessageQueue即线程用来维护线程产生的消息的消息队列,而这个 队列的调度则是由Looper来完成的。Looper负责将产生的消息放入队列,并及时的将合适的消息从队列中取出并交由合适的接受者处理。处理消息的便 是每个线程内部的Handler对象,特别是在UI线程中,由于Handler与UI处于同一个线程,所以我们就可以通过Handler处理接收到的消息 并及时更新UI中的组件。

     

    上述描述即可以实现在其它线程中完成耗时操作并在UI线程中通过Handler更新组件以反映耗时操作的处理效果。

    以下是它们之 间关系的图示:



    理解概念:

    Message:消息,其中包含了消息ID,消息处理对象以及处理的数据等,由MessageQueue统一列队,终由Handler处理。
    Handler:处理者,负责Message的发送及处理。使用Handler时,需要实现handleMessage(Message msg)方法来对特定的Message进行处理,例如更新UI等。
    MessageQueue:消息队列,用来存放Handler发送过来的消息,并按照FIFO规则执行。当然,存放Message并非实际意义的保存,而是将Message以链表的方式串联起来的,等待Looper的抽取。
    Looper:消息泵,不断地从MessageQueue中抽取Message执行。因此,一个MessageQueue需要一个Looper。
    Thread:线程,负责调度整个消息循环,即消息循环的执行场所。

     

    简单关系:


     

    Handler,Looper和MessageQueue就是简单的三角关系。

    Looper和MessageQueue一一对应,线程和looper也是一一对应,创建一个Looper的 同时,会创建一个MessageQueue。

    而Handler与它们的关系,只是简单的聚集关系,即Handler里会引用当前线程里的特定Looper 和MessageQueue。
    这样说来,多个Handler都可以共享同一Looper和MessageQueue了。

    当然,这些Handler也就运行在同一个线程里。

     

     

    消息循环过程:

    生成消息

    1 Message message = handler.obtainMessage();  
    2 message.arg1 = id;  
    3 message.obj = drawable;  
    4 handler.sendMessage(message);  

    发送消息

     
     1      /** 
     2      * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b> 
     3      * to this handler. 
     4      * @param uptimeMillis The absolute time at which the message should be 
     5      *         {@link android.os.SystemClock#uptimeMillis} time-base. 
     6      * @return Returns true if the message was successfully placed in to the  
     7      *         looper processing the message queue is exiting.  Note that a 
     8      *         the looper is quit before the delivery time of the message 
     9      */  
    public boolean sendMessageAtTime(Message msg, uptimeMillis)
    10 { 11 sent = ; 12 MessageQueue queue = mQueue; 13 (queue != ) { 14 msg.target = ; 15 sent = queue.enqueueMessage(msg, uptimeMillis); 16 } 17 { 18 RuntimeException e = RuntimeException( 19 + , e.getMessage(), e); 20 } 21 sent; 22 }

     在 Handler.java的sendMessageAtTime(Message msg, long uptimeMillis)方法中,我们看到,它找到它所引用的MessageQueue,然后将Message的target设定成自己(目的是为了在 处理消息环节,Message能找到正确的Handler),再将这个Message纳入到消息队列中。

     

    Looper从消息队列中抽取消息

    /** 
     1      public  static final void  loop() {  
     2         Looper me = myLooper();  
     3         MessageQueue queue = me.mQueue;  
     4          () {  
     5             Message msg = queue.next();   
     6               
     7               
     8               
     9              (msg != ) {  
    10                  (msg.target == ) {  
    11                       
    12                     ;  
    13                 }  
    14                  (me.mLogging!= ) me.mLogging.println(  
    15                                              );  
    16                 msg.target.dispatchMessage(msg);  
    17                  (me.mLogging!= ) me.mLogging.println(  
    18                                             + msg.callback);  
    19                 msg.recycle();  
    20             }  
    21         }  
    22     }  

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

    处理

     
     1     /** 
     2      * Handle system messages here. 
     3      */  
     4     public void dispatchMessage(Message msg) {  
     5         if (msg.callback != null) {  
     6             handleCallback(msg);  
     7         } else {  
     8             if (mCallback != null) {  
     9                 if (mCallback.handleMessage(msg)) {  
    10                     return;  
    11                 }  
    12             }  
    13             handleMessage(msg);  
    14         }  
    15     }  
    在 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将不能接收处理消息。在这种情况下,通用的作法是:

     
     1  class LooperThread extends Thread {  
     2       public Handler mHandler;  
     3       public void run() {  
     4             Looper.prepare();  
     5             mHandler = new Handler() {  
     6                public void handleMessage(Message msg) {  
     7                // process incoming messages here  
     8                }  
     9             };  
    10             Looper.loop();  
    11       }  
    12 } 

    在创建Handler之前,为该线程准备好一个Looper(Looper.prepare),然后让这个Looper跑起来(Looper.loop),抽取Message,这样,Handler才能正常工作。
    因此,Handler处理消息总是在创建Handler的线程里运行。而我们的消息处理中,不乏更新UI的操作,不正确的线程直接更新UI将引发异常。因此,需要时刻关心Handler在哪个线程里创建的。

     

         1,MessageQueue与Message的关系

    Message中文是消息,及线程处理的最小单元,里面带有处理的数据集,或还带有操作,及告诉目的地要做什么事情,   每个MessageQueue里包含有Message。每个Message不是直接插入到MessageQueue里的,而是通过MessageQueue.IdleHandler 与looper一起工作,把Message放到MessageQueue里,及addIdleHandler(MessageQueue.IdleHandler handler) 和removeIdleHandler(MessageQueue.IdleHandler handler) 方法,把MessageQueue.IdleHandler压入到MessageQueu里。

     

            2,Thread和HandlerThread的关系

            HandlerThread就是带有Looper循环的Thread.

     

            3,Looper介绍

            Looper即一个循环,必须绑定到一个固定的线程里,通过getThread()方法可以获得该looper绑定的Thread对象,通过 getMainLooper()获得主线程里的looper对象,myLooper()可以获得当前线程里的looper对象,通过myQueue()方 法可以获得当前线程里的messageQueue对象。

           

            4,Thread、HandlerThread、和Looper的关系的关系

            一般声明一个thread是没有带looper循环的,但是可以通过Looper.prepare()方法给一个线程加上 looper;Looper.loop()执行该looper绑定的线程里的messageQueue,直到该loopger结 束;Loop.quit()结束该循环,实例代码如下:

    
    
     1  class LooperThread extends Thread {
     2       public Handler mHandler;
     3       
     4       public void run() {
     5           Looper.prepare();
     6           
     7           mHandler = new Handler() {
     8               public void handleMessage(Message msg) {
     9                   // process incoming messages here
    10               }
    11           };
    12           
    13           Looper.loop();
    14       }
    15   }
    
    

         

        5,Handler和Thread HandlerThread Looper Message MessageQueue的关系

    它把消息发送到该生成它的线程里的MessageQueue里,然后当Message出队列的时候,就执行该消息。
    post(Runnable), postAtTime(Runnable, long), postDelayed(Runnable, long),是用来处理runnable对象的;
    sendEmptyMessage(int), sendMessage(Message), sendMessageAtTime(Message, long), and sendMessageDelayed(Message, long)方法是
    可以通过handleMessage (Message msg) 方法来接收消息。
    实例化:Handler(Looper looper) 



    二. 多线程与异步

    Main Thread & UI Thread

    当程序启动的时候android会自动创建一个进程和一个线程,这个线程负责界面更新,收集系统事件和用户的操作事件等并分配给对应的组件,所以这个线程非常重要 被称为主线程,因为所的和UI有关的操作都是在这个线程当中进行的所以也被称作UI线程。所以说默认情况下主线程和UI线程指的是同一个线程。

    Android的UI系统参考了很多SWT的设计模式。比如,UI线程机制,底层JNI实现 etc.

    Android 的UI系统是非线程安全的,意思是说只有创建UI的线程(也就是主线程)才可以对UI进程操作。如果我们在其它线程中执行了一些操作,而这些操作的结果又需要通过UI展现给用户,必需把这更新UI的操作转移到到UI线程中执行。

    很多Java起步的程序员对UI线程中的"消息循环"会感觉陌生。其实就是在线程内部有一个死循环一直监听系统事件(用户的操作等)并把任务分发给对应的 组件(有兴趣的可以看看NDK附带的native-activity的一个sample,在c中的实现方式就是一个while(1)的死循环)。主线程通 过Looper实现了消息的循环处理。

    The ralationship between Handler,Message,MessageQueue and Looper?

    如果一个线程里边有一个死循环,那么这个循环就会一直在死循环里边循环,并且这个线程不会过多的cpu资源,那么这个线程肯定的阻塞的。如果线程只是一直 循环没有什么意义,实际情况通常需要线程根据不同的条件运行不同的方法。我们通常的作法可能会加一个switch开关,根据条件的不同去调用不同的方法。

    线程间通信(最常见的就是,worker线程需要更新UI),这个时候实际是包含两个内容:一,数据的传递;二,方法的传递; Android中的Message用于数据传递,而Handler就是方法传递(其实是方法的执行者,Handler会把这个方法放到Handler所在 的线程当中去执行);MessageQueue是Message的容器和Java中的Queue一样都是容器,只不过Message Queue是专门用于装载Message的容器。Looper则是一个线程中的死循环用于管理MessageQueue,它负责阻塞读取 MessageQueue中的信息(如果MessageQueue中没有信息会一直在那里),挨个读取并把这个Message分发给对应的Handler 去执行。

     

    首先看一下mHandler.obtainMessage(SHOW_ALERT, Greetings).sendToTarget();会执行什么。

    通过查看Handler的源码发现执行了下边的代码。

    1 public final Message obtainMessage(int what, Object obj){
    2     return Message.obtain(this, what, obj);
    3 }
     
     
     接着查看Message中的代码
     1 public static Message obtain(Handler h, int what, Object obj) {
     2     Message m = obtain();
     3     m.target = h;
     4     m.what = what;
     5     m.obj = obj;
     6     return m;
     7 }
     8 /**
     9 * Return a new Message instance from the global pool. Allows us to
    10 * avoid allocating new objects in many cases.
    11 */
    12 public static Message obtain() {
    13     synchronized (mPoolSync) {
    14         if (mPool != null) {
    15             Message m = mPool;
    16             mPool = m.next;
    17             m.next = null;
    18             return m;
    19         }
    20     }
    21     return new Message();
    22 }

      看到这里我们应该明白为什么Google建议我们使用obtainMessage而不是new一个Message.这也是符合了Android中的对象回收机制。

     1 public void sendToTarget() {
     2 
     3     target.sendMessage(this);//targe就是一个Handler
     4 
     5 }
     6 sendMessage()会调用下面的sendMessageAtTime()把Message加入MessagQueue;
     7 
     8 public boolean sendMessageAtTime(Message msg, long uptimeMillis){
     9     boolean sent = false;
    10     MessageQueue queue = mQueue;
    11     if (queue != null) {
    12         msg.target = this;
    13         sent = queue.enqueueMessage(msg, uptimeMillis);
    14     }else {
    15         RuntimeException e = new RuntimeException(
    16                 this + " sendMessageAtTime() called with no mQueue");
    17         Log.w("Looper", e.getMessage(), e);
    18     }
    19     return sent;
    20 }

     到此我们的Message对象被加入到了Handler所在线程(也就是主线程)中的MessageQueue这个存储和管理Message的容器当中。下一步就该由Looper接手一个一个的取出Message对象处理了。Looper最主要的方法就是loop()方法

     

     1 public static final void loop() {
     2     Looper me = myLooper();
     3     MessageQueue queue = me.mQueue;
     4     while (true) {//死循环
     5         Message msg = queue.next(); // might block
     6            if (msg != null) {
     7             if (msg.target == null) {//退出死循环的机制
     8             // No target is a magic identifier for the quit message.
     9         return;
    10         }
    11          if (me.mLogging!= null) me.mLogging.println(
    12             ">>>>> Dispatching to " + msg.target + " "
    13                  + msg.callback + ": " + msg.what
    14         );
    15         msg.target.dispatchMessage(msg);//target of a msg is a Handler
    16         if (me.mLogging!= null) me.mLogging.println(
    17              " Finished to    " + msg.target + " "
    18             + msg.callback);//msg.callback is a Runnable
    19             msg.recycle();
    20         }
    21     }
    22 }

     Looper会一直阻塞读取MessagQueue中的Message对象(Message msg = queue.next()),当读取到一个Message时就会调用msg.target.dispatchMessage(msg)把这个 Message分发给对应的Handler去处理,等Handler把任务处理完了再接着读取下一个Message对象并处理。

     1 /**
     2 * Handle system messages here.
     3 */
     4 public void dispatchMessage(Message msg) {
     5     if (msg.callback != null) {
     6         handleCallback(msg);// call the run method of the runnable object I guess
     7     } else {
     8         if (mCallback != null) {
     9             if (mCallback.handleMessage(msg)) {
    10             return;
    11             }
    12         }
    13         handleMessage(msg);
    14     }
    15 }

     最后回来了我们的handlerMessage(msg);这个方法中。如果是用的不是sendMessage是是Hadler.post(Runnable)会调用

    
    
    1 private final void handleCallback(Message message) {
    2     message.callback.run();
    3 }
    
    

    这就是为什么在Handler中post的Runnable不会开启一个新的线程的原因。经过上面的追踪我们应该能明白不是所有 的线程都可以有Handler去执行handlerMessage或者处理Runnalbe的。其必要条件就是这个线程要有一个一直循环的 Looper.Looper一直循环肯定要有一个方法可以退出循环。

     

    Thread,Looper,Handler,Message,MessageQueue之间的关系 - Gobby.X - Just Miss.Gobby

    当我们调用msg.sendToTarget()的时候,我们的msg对象应付被压入MessageQueue的尾部。Looper在 MessageQueue的另一端一个一个的读取信息并处理。根据msg的target属性把cpu分配给对应的Handler去执行任务,这时 Handler会根据msg中的属性调用msg.handleMsg()或者msg.callback.run();当一个msg中的消息处理完返回之后 Looper会把这个msg放入msgPool当中方便下次再重复利用。从而减少对象的创建。Looper再从MessageQueue中读取下一个信 息,如此反复。。

    理解了这些其实就不难想象ANR是如何实现的了。当用户执行操作(比如点击了一个按钮)系统会生成一个Message对象,把用户操作的信息写入 Message对象,并把这个Message对象压入MessageQueue队列的尾部。系统过一段时间(一般是五秒)后会再来检查,刚刚放入的信息是 不是已经被处理了,如果信息还在队列中就表明。处理前面信息的过程当中发生的阻塞,用户的操作没有及时得到响应。系统弹出ANR对话框。

     

    作个总结:

      因为UI线程需要保持一直运行的状态,所以要有一个循环保持这个线程不会死掉,但这个线程又必需阻塞,以减少cpu的消耗。android中的这个循 环就是通过Looper实现的。有了这个 Looper,Looper就占据了整个线程,导致所有的方法想在些线程中运行就必需通过这个Looper,所以要有个方法可以进入这个Looper的内 部。MessageQueue就担当了这个通道 的角色。Message担当了集合的角色。所有在UI线程中运行的方法都必需通过MessageQueue进入Looper内部,不管 是用户定义的方法还是系统事件包括onCreate(),onStop(),用户点击事件etc..

     

     

    三.Android中消息系统模型和Handler Looper

    消息系统的基本原理和构成

           从一般的消息系统模型的建立大致构成以下几个部分:

        l  消息原型

        l  消息队列

        l  发送消息

        l  消息循环

        l  消息获取

        l  消息派发

        l  消息处理

     

    大致模型图如下:

        

     

           消息系统模型一般会包括以上七个部分(消息原型,消息队列,消息发送,消息循环,消息获取,消息派发,消息处理)。实际上的核心是消息队列和消息循环,其余部分都是围绕这两部分进行的。

      从前面文档的分析中我们知道Handler就是用来建立消息处理的系统模型,那么和这里基本消息系统模型相比,那么Handler又是如何囊括这七个部分的呢?

      在Android中对这六个部分进行了抽象成四个独立的部分:

        Handler,Message,MessageQueue,Looper;

    •   Message就是消息原型,包含消息描述和数据,
    •   MessageQueue就是消息队列,
    •   Looper完成消息循环
    •   Handler就是驾驭整个消息系统模型,统领Message,MessgeQueue和Looper;

     

     Handler能够实现消息系统模型,那么具体是如何进行工作的呢,下面探究一下这其中工作的方法和原理。

     

     

     

     

    四. Handler工作原理分析

      要了解Handler工作原理,先看一下这个系统模型具体组成的层次结构框架是个什么样的。

     

          

     

      实现Thread的消息循环和消息派发,缺省情况下Thread是没有这个消息循环的既没有Looper;需要主动去创建,然后启动Looper的消息循环loop;与外部的交互通过Handler进行;消息队列,由Looper所持有,但是消息的添加是通过Handler进行;

      

      消息循环和消息队列都是属于Thread,而Handler本身并不具有Looper和MessageQueue;但是消息系统的建立和交互,是Thread将Looper和MessageQueue交给某个Handler维护建立消息系统模型。所以消息系统模型的核心就是Looper。消息循环和消息队列都是由Looper建立的,

     

    而建立Handler的关键就是这个Looper。

      一个Thread同时可以对应多个Handler,一个Handler同时只能属于一个Thread。Handler属于哪个Thread取决于Handler在那个Thread中建立。

      在一个Thread中Looper也是唯一的,一个Thread对应一个Looper,建立Handler的Looper来自哪个Thread,Handler属于哪个Thread。

      故建立Thread消息系统,就是将Thread的Looper交给Handler去打理,实现消息系统模型,完成消息的异步处理。

      

    Handler与Thread及Looper的关系可以用下面图来表示:

        

     

           Handler并不等于Thread,必须通过Thread的Looper及其MessageQueue,用来实现Thread消息系统模型,依附于Thread上。

     

    在线程建立Handler时:

      使Handler满足消息系统需要的条件,将Thread中的Looper和MessageQueue交给Handler来负责维护。

    在线程中建立Handler,需要做以下工作:

      l  获取Thread中的Looper交给Handler的成员变量引用维护;

      l  通过Looper获取MessageQueue交给Handler的成员变量引用维护。

     

      那么消息系统模型建立完成之后,按照消息系统运行,从Handler来看就是发送消息派发消息,与此线程消息系统的交互都由Handler完成。

    消息发送和派发接口:

      l  post(runnable)消息,Runnable是消息回调,经过消息循环引发消息回调函数执行;

      l  sendMessage(Message)消息,经过消息循环派发消息处理函数中处理消息;

      l  dispatchMessage       派发消息,若是post或带有回调函数则执行回调函数,否则执行

      消息处理函数Handler的handleMessage(通常派生类重写)。

     

      以上就是Handler如何实现Thread消息系统模型的大致介绍, 下面将具体分析是如何实现消息系统模型运行的。

     

     

     

     

    五. Handler实现流程分析

     

      我们知道Handler就是一个消息系统的外壳,属于某个Thread并包装了Thread的Looper及其MessageQueue;与外部进行交互(同一个线程内或者线程之间),接收派发和处理消息,消息系统模型的核心是Looper。下面看看Handler是如何建立跑起来的,以msg消息为例,runnable实质是一样。

     

    1 Handler的建立


     

      Handler唯一属于某个Thread,在某个Thread中建立Handler时,需要获取Thread的Looper及其MessageQueue,建立Handler关键是Looper的来源。

        Handler提供了好几个构造函数但其本质一致:

    由外部传入Looper:当前线程或其他线程    

      public Handler(Looper looper) {
            //初始化构建消息系统参数
                  mLooper = looper;
                  mQueue = looper.mQueue;
                  mCallback = null;
      }     

    从当前线程获取:由创建Handler的Thread决定

           

    复制代码
      public Handler() {
            //初始化构建消息系统参数
                  mLooper = Looper.myLooper();
                  mQueue = mLooper.mQueue;
                  mCallback = null;
      }
    
      public static Looper myLooper() {
            return sThreadLocal.get();
        }
    复制代码

      不管哪种方式,我们知道Thread在默认情况下是没有建立消息循环Looper实例的。

         要实现消息循环必须确保Thread的Looper建立。如何确保呢?

      Looper提供了静态函数:

    复制代码
    public static void prepare() {
         if (sThreadLocal.get() != null) {
                  throw new RuntimeException("Only one Looper may be created per thread");
         }
    
         sThreadLocal.set(new Looper());
    }
    
    //存储线程的局部变量
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    复制代码

          每一个线程调用Looper.prepare时,都会创建为其唯一的Looper。

         要建立Handler,需要先创建线程的Looper,才能建立消息系统模型。通过Looper我们建立了

    Thread上的消息系统模型Handler,可以来进行消息系统的一系列流程了。

     

    2 消息发送


     

    消息发送两种方式:post和sendMessage;

           post:针对runnable对象;Runnable是一个接口,就是一个回调函数(提供了run方法)

           sendMessage:针对Message对象;

           

           下面通过代码具体看一下这个过程:

    复制代码
    public final boolean post(Runnable r){
           return  sendMessageDelayed(getPostMessage(r), 0);
    }
    
    public final boolean sendMessage(Message msg){
        return sendMessageDelayed(msg, 0);
    }
    复制代码

     

      看到post和sendMessage发送消息时,仅仅是对象不同而已,Runnable和Message;

    但实际上都是Message的形式来描述。

     

    这跟我通常理解的消息机制不同:

      通常post消息是将消息加入到消息队列中并不立即执行就返回,send消息是立即执行等待消息执行完才返回。

      而这里post或者send都是将消息放入到消息队列中,然后立即返回,等待消息循环时获取消息被执行。

     

      这里提供了众多的消息发送方法来指定消息的执行时间和顺序,具体可以查看源代码。

           消息执行顺序是根据消息队列中消息的排列顺序而定。

      下面看一下发送消息后将消息加入到消息队列中的代码:

    由Handler调用MessageQueue的enqueueMessage方法:

           

     1 final boolean enqueueMessage(Message msg, long when) {
     2 
     3               Message p = mMessages;
     4 
     5               if (p == null || when == 0 || when < p.when) {
     6                  msg.next = p;
     7                  mMessages = msg;
     8               }
     9               else {
    10 
    11                      Message prev = null;
    12                      while (p != null && p.when <= when) {
    13                             prev = p;
    14                             p = p.next;
    15                      }
    16 
    17                      msg.next = prev.next;
    18                      prev.next = msg;
    19               }
    20               ……
    21   }

     

    可以看到是按照时间顺序将消息加入到MessageQueue中;

    现在将消息加入到消息队列中存储起来,消息并未得到处理,下一步必然是如何派发消息和处理消息。

     

    3 消息派发


     

    建立Thread消息循环由Looper完成,存在一个消息调度死循环:    

    复制代码
      public static void loop() {
           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;
                         }
    
                         //派发消息 到target(Handler)
                msg.target.dispatchMessage(msg);
    
                //回收Msg到msgPool
                         msg.recycle();
                  }
           }
      }
    复制代码

      

    这里看到消息派发是由Message的target完成,这个target是什么呢?是一个Handler。

    消息系统是通过Handler用来与外部交互,把消息派发出去。可以看到没有这个Handler,消息循环将结束。

     

    消息派发由Looper通过Handler完成:

    复制代码
      public void dispatchMessage(Message msg) {
    
           //首先判断runnable对象
           if (msg.callback != null) {
                  handleCallback(msg);
           }
           else {
                  //整个消息系统的回调函数 可以不用实现自己Handler
                  if (mCallback != null) {
                         if (mCallback.handleMessage(msg)) {
                                return;
                         }
                  }
    
                  //消息处理 通常交给Handler派生类
                  handleMessage(msg);
           }
      }
    复制代码

     

      通过消息派发,这样就实现消息的异步处理。

     

    4 消息原型


     

    前面看到消息发送有两种方式:

      post(Runnable对象),sendMessage(Message对象),而中间都是通过Message对象保存在MessageQueue中。然后消息派发时处理方式不同。如果在sendMessage时将将消息对象附上Runnable对象,则post和sendMessage没有区别了。所以这两种方式很好理解基本一致,处理的方式不同罢了。

      消息系统模型中,我们的真正的消息原型是什么,都具有那些功能,下面看一下Message中到底包含了那些东西,能有效帮助我们合理的运用消息系统来完成一些任务和处理。

    Message消息原型:

    复制代码
      public final class Message implements Parcelable {
             //标识消息
             public int what;
             int flags;
             long when;
          
             //传递简单数据
             public int arg1;
             public int arg2;
        
             //传递较复杂数据 对象
             public Object obj;
             Bundle data;
    
             //处理消息的目标Handler
             Handler target;   
    
             //消息派发时 执行的Runnable对象
             Runnable callback;  
    
             //使消息形成链表
             Message next;
    
             //建立一个消息pool,回收msg,以避免重复创建节约开销
             private static Message sPool;
             private static int sPoolSize = 0;
             private static final int MAX_POOL_SIZE = 10;
      }  
    复制代码

      

      看到提供了很丰富的属性来描述消息,针对具体问题选择使用那些属性去怎么样描述消息了。

      获取新的Message对象时,Message提供了obtain方法:避免我们自己去分配Message新的对象,通过obtain获取,可能从MessagePool中获取,节约开销。

     

    下面看一下这个MessagePool是如何建立的:

      通常消息处理完毕的时候,消息也基本上处于无用状态可以释放回收了。对于需要频繁的创建释放的对象来说,创建和释放类实例都是要开销的,太频繁的使开销增大不好,像Message这种很有可能会频繁的创建。

     

      于是我们可以将创建的对象用完之后保存在一个Pool里面,以便再重复利用节约频繁创建释放开销。是如何建立的呢?必然是在消息处理完毕之后才能进行。

    MessagePool建立:

    复制代码
    public static void loop() {
           while (true) {
                  //派发消息
                  msg.target.dispatchMessage(msg);
    
                  //消息处理完毕 回收
            msg.recycle();
        }
    }     
    
    public void recycle() {
           //回收Message 建立全局的MessagePool
           if (sPoolSize < MAX_POOL_SIZE) {
               next = sPool;
               sPool = this;
               sPoolSize++;
           }
    }
    复制代码

     

     

    六. Handler的应用

      以上这就是整个Handler作用及消息系统模型的建立。

    使用也非常简单,虽然有很多方式,但只要理解Handler是建立在Looper上,实现Thread的

    消息系统处理模型,实现消息异步处理,我想对与Handler基本应用上没有什么不能理解的了。

    其他方面可以去看源码了。

      Handler使用起来是非常简单的,关键就是如何利用消息的异步处理,来合理的完成我们

    需要功能和任务。对于一个Thread,我们使用好几个Handler来进行异步处理,也可以创建新的Thread,

    通过Handler来实现消息异步处理等等,应用场景很多如何用的好用的合理,这就没什么经验了。

      至于如何使用,源码中很多例子可以看一下AsyncQueryHandler这个类,其中两个线程,

    完成查询工作,通过Handler进行线程之间有消息传递。感觉这个利用的很好很巧妙。

     

  • 相关阅读:
    jquery 操作单选框,复选框,下拉列表实现代码
    使用NewtonSoft.JSON.dll来序列化和发序列化对象
    c# HttpWebRequest与HttpWebResponse 绝技
    从新浪微博的改版谈网页重构
    选择GET还是POST?
    ckeditor3.0.1上传图片功能
    EM算法入门相关文章翻译与总结3
    EM算法入门相关文章翻译与总结2
    EM算法入门相关文章翻译与总结1
    PLSA中的EM算法
  • 原文地址:https://www.cnblogs.com/neo-java/p/7017843.html
Copyright © 2011-2022 走看看