Android消息队列模型

来源:互联网 发布:openwrt安装mysql 编辑:程序博客网 时间:2024/05/19 23:54

Google官方给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. When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it -- from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue.

There are two main uses for a Handler: (1) to schedule messages and runnables to be executed as some point in the future; and (2) to enqueue an action to be performed on a different thread than your own.

Scheduling messages is accomplished with the post(Runnable)postAtTime(Runnable, long)postDelayed(Runnable, long),sendEmptyMessage(int)sendMessage(Message)sendMessageAtTime(Message, long), and sendMessageDelayed(Message, long) methods. The post versions allow you to enqueue Runnable objects to be called by the message queue when they are received; the sendMessage versions allow you to enqueue a Message object containing a bundle of data that will be processed by the Handler's handleMessage(Message) method (requiring that you implement a subclass of Handler).

When posting or sending to a Handler, you can either allow the item to be processed as soon as the message queue is ready to do so, or specify a delay before it gets processed or absolute time for it to be processed. The latter two allow you to implement timeouts, ticks, and other timing-based behavior.

When a process is created for your application, its main thread is dedicated to running a message queue that takes care of managing the top-level application objects (activities, broadcast receivers, etc) and any windows they create. You can create your own threads, and communicate back with the main application thread through a Handler. This is done by calling the same post or sendMessage methods as before, but from your new thread. The given Runnable or Message will then be scheduled in the Handler's message queue and processed when appropriate.


消息队列模型:


消息系统模型一般包括以上七个部分:消息原型、消息队列、消息发送、消息循环、消息获取、消息派发、消息处理。对应关系如下:

1.  Message为消息原型,包含消息描述和数据

2.  MessageQueue为消息队列

3.  Looper完成消息循环

4.  Handler驾驭整个消息系统模型,统领Message、MessageQueue和Looper

三 Handler工作原理分析

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

 

      

Looper:

  实现Thread的消息循环和消息派发,缺省情况下Thread是没有这个消息循环的既没有Looper;

需要主动去创建,然后启动Looper的消息循环loop;与外部的交互通过Handler进行;

MessageQueue:

  消息队列,由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一个独立的类,又不属于某个Thread,而这里创建Looper的函数又是静态的,

属于整个Looper类;创建Looper之后交给静态成员变量sThreadLocal保存,获取

sThreadLocal.get(),那么一个静态变量属于整个类,属性更改始终有效。一次创建之后

sThreadLocal.get()永远都不等于null!

  

  而Thread和Looper是唯一对应的,那这里岂不是所有的Thread都是用同一个Looper,不可能!

所以肯定这个ThreadLocal是有玄机的。网上一查:

  ThreadLocal:

    维护线程的变量,为每个使用该变量的线程实例提供独立的变量副本,每个线程都能够独立使用该变量,

    而互不影响。(详细可参考:http://blog.csdn.net/qjyong/article/details/2158097)

  所以每一个线程调用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方法:

       

复制代码
  final boolean enqueueMessage(Message msg, long when) {              Message p = mMessages;              if (p == null || when == 0 || when < p.when) {                 msg.next = p;                 mMessages = msg;              }              else {
Message prev
= null; while (p != null && p.when <= when) { prev = p; p = p.next; } msg.next = prev.next; prev.next = msg; } ……  }
复制代码

  

  可以看到是按照时间顺序将消息加入到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++;       }}
复制代码



0 0
原创粉丝点击