消息发送
创建消息
创建消息使用下面的代码:
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两种方案,
- send方案发送消息
- sendMessage(Message) 立即发送Message到消息队列
- sendMessageAtFrontOfQueue(Message) 立即发送Message到队列,而且是放在队列的最前面
- sendMessageAtTime(Message,long) 设置时间,发送Message到队列
- sendMessageDelayed(Message,long) 延时若干毫秒后,发送Message到队列
- 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的方法。
整个流程可以用下面的图片表示:
总结:遍历消息队列中的所有消息,根据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)中。