Android Handler源码之消息发送

消息发送

创建消息

创建消息使用下面的代码:

Message msg = Message.obtain(); // 实例化消息对象
msg.what = 1; // 消息标识
msg.obj = "AA"; // 消息内容存放
handler.sendMessage(msg);

创建消息,获取Message对象,最好的方法是调用Message.obtain(),而不是直接通过new Message()的方式,因为Message.obtain()是从一个可回收的对象池中获取Message对象,避免了重复创建。

关于Message的源码分析可以看下这篇文章:
Messgae源码分析

发送消息

查看一下Handler的类结构图:




Handler发送消息主要有sendMessage和post两种方案,

  1. send方案发送消息
  • sendMessage(Message) 立即发送Message到消息队列
  • sendMessageAtFrontOfQueue(Message) 立即发送Message到队列,而且是放在队列的最前面
  • sendMessageAtTime(Message,long) 设置时间,发送Message到队列
  • sendMessageDelayed(Message,long) 延时若干毫秒后,发送Message到队列
  1. post方案
  • post(Runnable) 立即发送Message到消息队列
  • postAtFrontOfQueue(Runnable) 立即发送Message到队列,而且是放在队列的最前面
  • postAtTime(Runnable,long) 设置时间,发送Message到队列
  • postDelayed(Runnable,long) 在延时若干毫秒后,发送Message到队列

下面先从send方案中的第一个sendMessage() 开始源码跟踪。

send方式

sendMessage()

发送消息在执行了handler.sendMessage(msg)之后,我们往下追踪源码。
调用了下面的方法:

public final boolean sendMessage(Message msg)
{
    return sendMessageDelayed(msg, 0);
}

继续调用了sendMessageDelayed(msg, 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) {
 // 1
    MessageQueue queue = mQueue;
    if (queue == null) {
        RuntimeException e = new RuntimeException(
                this + " sendMessageAtTime() called with no mQueue");
        Log.w("Looper", e.getMessage(), e);
        return false;
    }
    // 2
    return enqueueMessage(queue, msg, uptimeMillis);
}

这个方法主要做了两件事,

  • 1、获取消息队列,并对该消息队列做非空判断,如果为null,直接返回。
  • 2、调用了enqueueMessage()方法,第一步获取的消息队列也作为了参数。

enqueueMessage()

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        // 1
    msg.target = this;
    // 2
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    // 3
    return queue.enqueueMessage(msg, uptimeMillis);
}

主要做了三件事:

  • 1、msg.target = this,设置了msg的target变量,将target指向自己。查看Message.java的源码,target实际上是Handler对象。
  • 2、mAsynchronous是控制着Handler发送同步异步消息的,在这里先简单提一下异步的作用,这里的异步主要是针对Message中的障栅(Barrier),当出现障栅(Barrier)时,同步消息会被阻塞,而异步消息不会被阻塞。后面会有单独的文章介绍Handler的同步消息和异步消息。这里mAsynchronous为false,mAsynchronous是控制着Handler发送异步消息的,在Handler初始化时,在Handler构造方法里,传了false:
public Handler() {
    this(null, false);
}

在下面Handler的构造方法里,将mAsynchronous设置为false。

public Handler(Callback callback, boolean async) {
    ......省略代码
    mLooper = Looper.myLooper();
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread " + Thread.currentThread()
                    + " that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

3、调用MessageQueue的enqueueMessage()方法。

继续看enqueueMessage()方法。

boolean enqueueMessage(Message msg, long when) {

        // 1 判断msg的target变量是否为null,如果为null则抛出异常
    if (msg.target == null) {
        throw new IllegalArgumentException("Message must have a target.");
    }
    
    // 2 判断msg的标志位,此时msg应该是要入队,msg的标志位应该是还未被使用,如果是已使用状态,很明显是有问题的,直接抛出异常
    if (msg.isInUse()) {
        throw new IllegalStateException(msg + " This message is already in use.");
    }
    
        // 3 加入同步锁
    synchronized (this) {
    
    // 4 判断消息队列是否是关闭状态,如果是关闭状态,return false消息入队失败,并且回收消息
        if (mQuitting) {
            IllegalStateException e = new IllegalStateException(
                    msg.target + " sending message to a Handler on a dead thread");
            Log.w(TAG, e.getMessage(), e);
            msg.recycle();
            return false;
        }
        
                // 5 设置msg的when,修改msg的标志位为已使用状态
        msg.markInUse();
        msg.when = when;
        Message p = mMessages;
        boolean needWake;
        // 6 主要是用来判断要入队的msg是否位于队列头部。第一个msg时,mMessages为null,所以该msg应该位于头部
        if (p == null || when == 0 || when < p.when) {
            // New head, wake up the event queue if blocked.
            // 把msg的下一个元素设置为p,p是mMessages,mMessages此
            //时为null,所以第一个message的next指向了null
            msg.next = p;
            // 把msg设置为链表的头部元素
            mMessages = msg;
            // needWake需要根据mBlocked的情况考虑是否触发,如果有阻
            // 塞,则需要唤醒,让它立刻去拿消息处理
            needWake = mBlocked;
        } else {  // 7 如果上面三个条件都不满足则说明要把msg插入到中间的位置
        // 8 ,如果头部元素不是障栅(barrier)或者异步消息,而且还是插入
        // 中间的位置,我们是不唤醒消息队列的
        needWake = mBlocked && p.target == null && msg.isAsynchronous();
            Message prev;
            // 9 不断遍历消息队列,根据when的比较找到合适的插入
            // Message的位置
            for (;;) {
                    // 设置前一个message指针
                prev = p;
                // 设置下一个message指针
                p = p.next;
                // 10
                if (p == null || when < p.when) {
                    break;
                }
                if (needWake && p.isAsynchronous()) {
                    needWake = false;
                }
            }
            // 11
            msg.next = p; // invariant: p == prev.next
            prev.next = msg;
        }

        // We can assume mPtr != 0 because mQuitting is false.
        // 12
        if (needWake) {
            nativeWake(mPtr);
        }
    }
    return true;
}
  • 第一步,判断msg的target变量是否为null,如果为null则抛出异常。
  • 第二步,判断msg的标志位,此时msg应该是要入队,msg的标志位应该是还未被使用,如果是已使用状态,很明显是有问题的,直接抛出异常。
  • 第三步,加入同步锁。
  • 第四步,判断消息队列是否是关闭状态,如果是关闭状态,return false消息入队失败,并且回收消息。
  • 第五步,设置msg的when,修改msg的标志位为已使用状态。
  • 第六步,主要是用来判断要入队的msg是否位于队列头部。如果p==null则说明msg是消息队列的第一个消息;when == 0 表示立即执行;when< p.when 表示 msg的执行时间早与链表中的头部元素的时间,所以上面三个条件,哪个条件成立,都要把msg设置成消息队列中链表的头部是元素。
  • 第七步,如果上面三个条件都不满足则说明要把msg插入到中间的位置。
  • 第八步,如果头部元素不是障栅(barrier)或者异步消息,而且还是插入中间的位置,我们是不唤醒消息队列的。
  • 第九步,进入一个死循环,mMessages是第一个message,prev = p,将p的值赋值给prev,前面p指mMessage,所以此处prev指向了mMessage,即队列中的第一个message。p = p.next,p指向了队列中的第二个message,以此类推,实现了消息指针的移动。
  • 第十步,跳出循环条件,p==null,说明没有下一个元素,即消息队列到末尾了。p!=null&&when < p.when 说明当前需要入队的这个message的执行时间是小于小于队列中p指向的message的执行时间的,也就是说这个需要入队的message比p指向的message先执行,说明这个位置刚好是这个入队的message的,这样就跳出循环。
  • 第十一步,跳出循环后,主要做了两件事,第一,将入队的这个msg的next指向循环中获取到的应该排在这个消息之后的message。第二,将msg前面的message.next指向了msg。这样msg就完成了入队。
  • 第十二步,如果需要唤醒,则唤醒,调用native的方法。

整个流程可以用下面的图片表示:


第一个message入队

第二个message入队

message入队

总结:遍历消息队列中的所有消息,根据when的比较,找到合适的message的入队位置。

sendMessageAtFrontOfQueue()

查看源码:

public final boolean sendMessageAtFrontOfQueue(Message msg) {
    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, 0);
}

和sendMessageAtTime()大体上一样,唯一的区别是该方法在调用enqueueMessage()方法时,最后一个参数是0。
查看MessageQueue的enqueueMessage()方法发现,当when==0时,会一直走if语句的内容,msg会一直插在消息队列的头部。

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{
    ......省略代码
}

sendEmptyMessage

sendEmptyMessage()方法,调用了sendEmptyMessageDelayed()方法

public final boolean sendEmptyMessage(int what)
{
    return sendEmptyMessageDelayed(what, 0);
}

sendEmptyMessageDelayed()方法,内阻组装了一个仅有what的Message,然后调用sendMessageDelayed(),从这不开始就跟sendMessage()往后的流程相同。

public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
    Message msg = Message.obtain();
    msg.what = what;
    return sendMessageDelayed(msg, delayMillis);
}

sendEmptyMessageAtTime

查看源码,直接调用了sendEmptyMessageAtTime()。

public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
    Message msg = Message.obtain();
    msg.what = what;
    return sendMessageAtTime(msg, uptimeMillis);
}

以上就是发送消息的send方案,最终都会走向了enqueueMessage()方法。


post方式

查看post方式的源码,可以看到,在方法内部,还是调用了sendMessageDelayed()方法,最终也会走到上面的send流程,最终调用enqueueMessage()方法。这其中有个getPostMessage()方法。

public final boolean post(Runnable r)
{
   return  sendMessageDelayed(getPostMessage(r), 0);
}

查看getPostMessage()的源码

private static Message getPostMessage(Runnable r) {
    Message m = Message.obtain();
    m.callback = r;
    return m;
}

这个方法主要做了两件事:

  • 通过Message.obtain()从消息对象池中获取一个空的messgae
  • 将这个空的message的callback变量指向Runnable

所以我们知道boolean post(Runnable r)方法的内置也是通过Message.obtain()来获取一个Message对象m,然后仅仅把m的callback指向参数r而已。最后最终通过调用send方案的某个流程最终调用到boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)

总结

Handler的发送消息,无论是通过send方案还是pos方案最终都会做走到 boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)中。

你可能感兴趣的:(Android Handler源码之消息发送)