萝卜小姐-Handler之系列sendMessage学习
16lz
2021-01-23
sendMessage方式学习
- sendMessage作用
- sendMessage主要方法以及源码学习
- sendMessage(Message)
- sendMessageDelayed(@NonNull Message msg, long delayMillis)
- sendMessageAtTime(@NonNull Message msg, long uptimeMillis)
- enqueueMessage(msg, uptimeMillis)
- sendMessage系列其他方法
- sendEmptyMessage(int what)
- sendEmptyMessageDelayed(int what, long delayMillis)
- sendEmptyMessageAtTime(int what, long uptimeMillis)
- sendMessageAtFrontOfQueue(@NonNull Message msg)
- executeOrSendMessage(@NonNull Message msg)
sendMessage作用
MessageQueue是一种单向链表数据结构,Message中有一个next属性指向队列中下一条消息,队列排序是根据Message的when字段(消息延迟时间)来排序的,调用handler.sendMessage(msg)发送的消息.
sendMessage主要方法以及源码学习
sendMessage(Message)
/** 在当前时间之前的所有挂起消息之后,将消息推送到消息队列的末尾。它将在{@link#handleMessage}中,在附加到此处理程序的线程中接收。 *如果消息已成功放入消息队列,@return返回true。失败时返回false,通常是因为处理消息队列的looper程序正在退出。 */ public final boolean sendMessage(@NonNull Message msg) { return sendMessageDelayed(msg, 0); }
- 由代码调用可知sendMessage() 内部调用的是sendMessageDelayed(msg,delayMillis),
- delayMillis = 0,
下一步,我们先来看sendMessageDelayed(@NonNull Message msg, long delayMillis)
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); }
- 由代码调用可知sendMessageDelayed() 内部调用的是sendMessageAtTime(msg,uptimeMillis),
- uptimeMillis = SystemClock.uptimeMillis() + delayMillis,
下一步,我们先来看sendMessageAtTime(@NonNull Message msg, long uptimeMillis)
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); }
enqueueMessage(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; }
总结:
- sendMessageAtTime()的time = sendMessageDelayed()的time + SystemClock.uptimeMillis();
- 消息进入队列是一个以时间排序的链表
- sendMessage()内部是将延迟时间=0,直接插入在链表的头部
- sendMessage -> sendMessageDelayed -> sendMessageAtTime -> enqueueMessage -> MessageQueue.enqueueMessage
下面我们继续来看看其他的消息发送方式
sendMessage系列其他方法
sendEmptyMessage(int what)
/** * 发送只包含what值的消息 * * @return 如果消息已成功放入消息队列,则返回true。失败时返回false,通常是因为处理消息队列的循环程序正在退出 */ public final boolean sendEmptyMessage(int what) { return sendEmptyMessageDelayed(what, 0); }
sendEmptyMessageDelayed(int what, long delayMillis)
/** * 发送只包含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); }
sendEmptyMessageAtTime(int what, long uptimeMillis)
/** *发送只包含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); }
sendMessageAtFrontOfQueue(@NonNull Message msg)
/** * * 在消息队列前面对消息进行排队,以便在消息循环的下一次迭代中处理。您将在{@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);/* 直接插入在链表头部 */ }
executeOrSendMessage(@NonNull Message msg)
/** *如果在该处理程序对应的同一线程上调用消息,则同步执行消息,否则{@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内部消息发送的学习,我们已经学完了,让我们实际创建学习一下吧!
更多相关文章
- Android应用程序键盘(Keyboard)消息处理机制分析(5)
- [转]android两次按返回键退出程序实现
- Android应用程序中模拟发送键盘触摸消息
- android 记录所有打开的Activity,退出程序
- android 彻底退出程序方法
- Android 4.0允许用户禁用所有系统自带程序
- NDK开发之环境的搭建和开发第一个NDK程序