【安卓笔记】Handler,Looper,MessageQueue,Message源码分析

这篇文章将分析android中的消息机制。
通过上一篇文章,我们了解到:
每一个handler其实都绑定了一个线程(Thread)和消息队列(MessageQueue),消息队列中存放的是一堆待处理的消息,Looper通过一个loop方法不断从消息队列中获取到消息(Message)(先进先出的方式),执行消息队列的出队方法,然后该消息会通过自身绑定target(其实是一个handler对象),分发处理携带的消息(dispatchMessage)。消息处理完成之后,该消息会被回收(recycle),回到消息池中。而handler的sendMessage方法其实会调用消息队列的入队方法(enqueueMessage),将包装好的消息加到消息队列中,再次循环。这其实就是android下的消息机制。本文将试图从源码的角度分析这一流程。

首先介绍Handler,Looper,MessageQueue,Message四个概念:
Message:消息,其中包含了消息ID,消息处理对象以及处理的数据等,由MessageQueue统一列队,终由Handler处理。
Handler:处理者,负责Message的发送及处理。使用Handler时,需要实现handleMessage(Message msg)方法来对特定的Message进行处理,例如更新UI等。
MessageQueue:消息队列,用来存放Handler发送过来的消息,并按照FIFO规则执行。当然,存放Message并非实际意义的保存,而是将Message以链表的方式串联起来的,等待Looper的抽取。
Looper:消息泵,不断地从MessageQueue中抽取Message执行。因此,一个MessageQueue需要一个Looper。
-------------------------------------
注:一个线程默认是没有消息队列的,如果我们要使用handler,必须指定Looper,Looper内部会创建MessageQueue。其中比较特殊的是UI线程提供了默认的Looper,我们不需要为其指定Looper。如果是其他线程,我们必须显式调用Looper.prepare和Looper.loop方法创建Looper并使其运转起来。
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调用post或者sendMessage方法开始,跟踪源码进行分析,从而了解整个消息机制的运作过程。
我们在调用sendMessage方法时,需要提供一个Message对象,Message对象用于封装一个消息,我们通过查看源码观察下该类的成员变量:
public final class Message implements Parcelable {
    public int what;
    public int arg1; 
    public int arg2;
    public Object obj;

    long when; 
    Bundle data;  
    Handler target;      
    Runnable callback;     
    // sometimes we store linked lists of these things
    Message next;
    private static final Object sPoolSync = new Object();
    private static Message sPool;
    private static int sPoolSize = 0;
    private static final int MAX_POOL_SIZE = 50;
    ... ...
四个公有变量相信大家都不陌生,这里就不再介绍了。我们看到Message还有一些成员变量,比如说target,这是个Handler对象,target可以通过set方法或者obtainMessage等方式设置,一旦设置了Target,这个message就被绑定到一个具体的handler上了,然后message内部即可调用sendToTarget方法将message交给绑定的Handler进行处理,内部也调用的是sendMessage:
public void sendToTarget() {
        target.sendMessage(this);
    }
callback对象是Runnable类型的,这个也比较好理解,因为我们的handler除了可以处理message之外,还可以通过Post方式将Runnable加入队列,进行处理。这个执行流程我们放在后面介绍。
我们还看到了一个Message类型的next,这个成员变量持有了下一个Message的引用,众多Message就是通过这种方式形成一个链表,也就是所谓的消息池,我们看到消息池默认的容量为0(sPoolSize?),池的最大容量为50。Message的obtain及其重载形式每次调用都会从池中取出一个Message,如果不存在就new一个出来。
public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }
recycle方法又会将用完的Message回收:
 public void recycle() {
        clearForRecycle();
        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }
【安卓笔记】Handler,Looper,MessageQueue,Message源码分析
现在假设我们的消息已经封装好了,下一步必然就是发送消息了。通过调用Handler的sendMessage方法可以发送一条消息。
public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }
sendMessage方法调用了sendMessageDelayed方法,延时设为0,继续跟源码:
public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }
先是判断延时参数的合法性,然后调用了sendMessageAtTime方法,果断跟进去看看:
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, uptimeMillis);
    }
跟到这一步,我们发现了一个没见过的变量,mQueue,查看该变量定义:
......
final MessageQueue mQueue;
final Looper mLooper;
final Callback mCallback;
......
原来mQueue是一个消息队列,那么消息队列是怎么初始化的呢?我们找到Handler的一个构造器:
public Handler(Callback callback, boolean async) {
      ... ...
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can‘t create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
原来mQueue是Looper的成员变量,这个构造器通过Looper.myLooper方法返回了一个与当前线程绑定的Looper对象,进而获取到与Looper绑定的MessageQueue。这是Looper的构造器:
private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mRun = true;
        mThread = Thread.currentThread();
    }
既然我们知道了mQueue的来历,那么我们继续随着上面的思路跟进吧!刚才我们通过跟踪sendMessage源码发现内部最终调用了sendMessageAtTime方法,而这个方法又调用了enqueueMessage方法,从名字上能看出,该方法是将消息出队的方法,等等。。消息出队不应该是消息队列的方法么?怎么Handler也有?别急,我们查看enqueueMessage方法源码:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;//我觉得这里应该判断target是否已经存在,如果存在就没必要设置了吧?
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }
这下原因显而易见了,handler的入队方法其实调用的是消息队列的入队方法。
下面我们来到MessageQueue源码,查找到enqueueMessage方法:
final boolean enqueueMessage(Message msg, long when) {
        if (msg.isInUse()) {//消息是否正在使用
            throw new AndroidRuntimeException(msg + " This message is already in use.");
        }
        if (msg.target == null) {//是否绑定handler
            throw new AndroidRuntimeException("Message must have a target.");
        }
        boolean needWake;
        synchronized (this) {
            if (mQuiting) {
                RuntimeException e = new RuntimeException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w("MessageQueue", e.getMessage(), e);
                return false;
            }
            msg.when = when;
            Message p = mMessages;
            if (p == null || when == 0 || when < p.when) {
                // New head, wake up the event queue if blocked.
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }
        }
        if (needWake) {
            nativeWake(mPtr);
        }
        return true;
    }
跟到这里终于明白了,这个MessageQueue是通过mMessages变量来记录当前待处理的消息,通过Mesage的发送的时间进行排序(调用msg.next)。
到这里整个入队操作就分析完了,通过这一系列的操作,handler将消息发送给了消息队列。到这里消息只是不断被加到消息队列中,我们并没有取出消息。那么Handler的handMessage方法又是怎么被调用的呢??带着这个疑问,我们来研究下Looper,消息泵,既然是泵,之所以叫泵,是因为它可以从消息队列中取出消息!之前说过,要在线程中使用Handler,必须调用Looper的prepare方法创建Looper对象,然后调用loop方法让消息队列运作起来。那么,这个loop方法是关键,我们查看该方法源码:
public static void loop() {
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn‘t called on this thread.");
        }
        final MessageQueue queue = me.mQueue;
        Binder.clearCallingIdentity();
        ... ...
        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                return;
            }
             ... ...
            msg.target.dispatchMessage(msg);
             ... ...
            msg.recycle();
        }
    }
这个方法首先获取到消息队列的一个引用,然后在一个死循环中反复调用消息队列的出队方法(next)获取到下一个待处理的消息的引用,然后调用该消息所绑定的handler的dispatchMessage方法分发消息,最后将该消息回收。我们跟踪Handler的dispatchMessage方法:
 public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }
既然是分发消息的方法,那必然会根据消息的类型做出不同的处理,这个方法正是根据Message对象是否携带了callback,如果携带了callback那就执行handleCallback方法,callback之前分析Message类的时候已经知道是一个Runnable的对象了。下面我们看看handleCallback是什么实现的:
 private static void handleCallback(Message message) {
        message.callback.run();
    }
 一目了然!直接调用callback的run方法。之前我们用post方法发送消息时携带的Runnable对象在Handle内部被转化为一个Message:
private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }
post方法中的Runnable对象被转成Message之后也会进入消息队列,最终被Looper抽取出来,让handler进行处理。
普通的sendMessage方法是不携带callback的,这时便进入dispatchMessage方法的else分支,进入else分支后,首先判断handler是否有一个CallBack的回调函数,如果有,直接调用回调函数的handMessage方法,这个CallBack是一个接口,它的好处是我们不用在继承Handler重写handleMessage方法了,CallBack接口定义:
 public interface Callback {
        public boolean handleMessage(Message msg);
    }
如果没有实现回调接口,那么便调用Handle的handMessage方法处理消息。
-------------------------------------------
通过上面的一番分析,我们终于明白了android下的消息机制的运作原理了,为了更形象的说明整个流程,这里贴一张示意图:
【安卓笔记】Handler,Looper,MessageQueue,Message源码分析



---------------------------------------------------------------------------------------

写一篇博客不容易,请尊重作者劳动成果,转载请注明出处

---------------------------------------------------------------------------------------

【安卓笔记】Handler,Looper,MessageQueue,Message源码分析,布布扣,bubuko.com

【安卓笔记】Handler,Looper,MessageQueue,Message源码分析

上一篇:Android屏幕适配


下一篇:服务端 I/O 性能大比拼:Node、PHP、Java 和 Go