MessageQueue是一种单向链表数据结构,Message中有一个next属性指向队列中下一条消息,队列排序是根据Message的when字段(消息延迟时间)来排序的,调用handler.sendMessage(msg)发送的消息.
/** 在当前时间之前的所有挂起消息之后,将消息推送到消息队列的末尾。它将在{@link#handleMessage}中,在附加到此处理程序的线程中接收。
*如果消息已成功放入消息队列,@return返回true。失败时返回false,通常是因为处理消息队列的looper程序正在退出。
*/
public final boolean sendMessage(@NonNull Message msg) {
return sendMessageDelayed(msg, 0);
}
下一步,我们先来看sendMessageDelayed(@NonNull Message msg, long delayMillis)
/* 在所有挂起的消息之后将消息推送到消息队列的末尾
*在(current time + delayMillis)之前。它将在{@link#handleMessage}中接收,
*在附加到此处理程序的线程中。
*@return如果消息成功放入消息队列。失败时返回false,通常是因为
*正在退出处理消息队列的循环程序。
*/
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
下一步,我们先来看sendMessageAtTime(@NonNull Message msg, long uptimeMillis)
/**
*在绝对时间(以毫秒为单位)之前,在所有挂起的消息之后将消息排入消息队列uptimeMillis。
time-base是{@link安卓系统时钟}.深度睡眠时间将增加执行的额外延迟。您将在{@link#handleMessage}中,在附加到此处理程序的线程中接收它。
*@param uptimemill是使用{@链接传递消息的绝对时间安卓系统时钟}time-base。
*如果消息已成功放入消息队列,@return返回true。失败时返回false,通常是因为处理消息队列的循环程序正在退出。注意,结果为true并不意味着消息将被处理——如果在消息传递时间之前循环器退出,那么消息将被丢弃。
*/
public boolean sendMessageAtTime(@NonNull 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);
}
sendMessageAtTime()内部调用的是enqueueMessage(),下面我们看一下Handler 的enqueueMessage()方法
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
/* If true, the handler calls {@link Message#setAsynchronous(boolean)}
*for each {@link Message} that is sent to it or {@link Runnable} that is posted to it.
*/
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
代码内容也很简单,调用了MessageQueue 的enqueueMessage().我们来看一下
/* 承接Handler发送消息部分,往队列中插入消息 */
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
/* 可以在任何线程中调用handler.sendMessage(msg3)发送消息,此处使用synchronized同步机制保证多线程并发时消息队列不会错乱 */
synchronized (this) {
/* 如果调用了Looper.quit()退出后,消息不会被插入 */
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;
}
msg.markInUse();/* 标记消息正在被使用 */
msg.when = when;/* when表示消息应该发送的时间,该时间=发送消息时距离手机启动毫秒数+消息延迟发送时间 */
Message p = mMessages;/* 队列头部的消息对象 */
boolean needWake;
/***************************************下面是message 进入messageQueue的核心内容**********************************************/
/* 队列头部元素为null(空队列) || 消息延迟时间 == 0 || 消息延迟时间 小于 头部队列的延迟时间 */
/* Handler.sendMessage(),它的最小值为0,表示消息需要立刻处理,如果不为0表示是延时消息,该时间值越小,则消息越早被取出处理 */
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;/* 新队头消息的next指向老的队头消息,由此可见这里是一个链表 */
mMessages = msg;/* mMessages指向新的头部消息 */
needWake = mBlocked;/* 是否需要唤醒消息轮询 */
} else {
/*如果此消息是延时消息,并且不能放到队头,则根据延迟时间将其插入到链表的合适位置,由此可见这是一个按时间排序的链表*/
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
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插入到队列中,前后进行交换 */
msg.next = p; // invariant:
p == prev.next
prev.next = msg;
}
/*************************************************************************************/
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);/* 如果轮询被阻塞,需要唤醒轮询 */
}
}
return true;
}
总结:
下面我们继续来看看其他的消息发送方式
/**
* 发送只包含what值的消息
*
* @return 如果消息已成功放入消息队列,则返回true。失败时返回false,通常是因为处理消息队列的循环程序正在退出
*/
public final boolean sendEmptyMessage(int what) {
return sendEmptyMessageDelayed(what, 0);
}
/**
* 发送只包含what值的消息,该值将在指定的时间段过后传递。
* @see #sendMessageDelayed(android.os.Message, long)
*
* @return 如果消息已成功放入消息队列,则返回true。失败时返回false,通常是因为处理消息队列的循环程序正在退出
*/
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}
/**
*发送只包含what值的消息,该消息将在特定时间传递。
* @see #sendMessageAtTime(android.os.Message, long)
*
* @return 如果消息已成功放入消息队列,则返回true。失败时返回false,通常是因为处理消息队列的循环程序正在退出
*/
public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageAtTime(msg, uptimeMillis);
}
/**
*
* 在消息队列前面对消息进行排队,以便在消息循环的下一次迭代中处理。您将在{@link#handleMessage}中,
* 在附加到此处理程序的线程中接收它。此方法只在非常特殊的情况下使用——它很容易导致消息队列不足、导致排序问题或产生其他意外的副作用
* @return 如果消息已成功放入消息队列,则返回true。失败时返回false,通常是因为处理消息队列的循环程序正在退出
*/
public final boolean sendMessageAtFrontOfQueue(@NonNull 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);/* 直接插入在链表头部 */
}
/**
*如果在该处理程序对应的同一线程上调用消息,则同步执行消息,否则{@link#sendMessage将其推送到队列}
* @return 如果消息已成功放入消息队列,则返回true。失败时返回false,通常是因为处理消息队列的循环程序正在退出
* @hide
*/
public final boolean executeOrSendMessage(@NonNull Message msg) {
if (mLooper == Looper.myLooper()) {
dispatchMessage(msg);
return true;
}
return sendMessage(msg);
}
当当当,关于Hander内部消息发送的学习,我们已经学完了,让我们实际创建学习一下吧!