Android 消息机制之深入学习MessageQueue
一,简述
MessageQueue在Android中指消息队列,顾名思义就是存放消息的消息池,但是它的内部实现并不是队列而是一个单链表,可能是单链表的删除和插入比较有优势吧。MessageQueue的内部对消息的主要操作就是插入,读取删除,不具备处理消息的能力。
二,源码分析
1,重要属性信息介绍
// True if the message queue can be quit.private final boolean mQuitAllowed;@SuppressWarnings("unused")private long mPtr; // used by native codeMessage mMessages;private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();private SparseArray<FileDescriptorRecord> mFileDescriptorRecords;private IdleHandler[] mPendingIdleHandlers;private boolean mQuitting;// Indicates whether next() is blocked waiting in pollOnce() with a non-zero timeout.private boolean mBlocked;
mQuitAllowed:是否允许MessageQueue退出
mPtr:可以理解成是C/C++的指针,是一个内存地址,当为0的时候说明消息队列被释放了
mMessage:表示存储消息链表的头Head
mQuitting:当前队列是否处于正在退出状态
mBlocked:是否被阻塞
2,构造方法
MessageQueue(boolean quitAllowed) {mQuitAllowed = quitAllowed;mPtr = nativeInit();}
可以看到创建MessageQueue的时候就需要指定MessageQueue是否可以退出。nativeInit();是一个Jni方法用来初始化mPtr
3,enqueueMessage(Message msg,long when)
boolean enqueueMessage(Message msg, long when) {msg.target 指消息机制中的Handlerif (msg.target == null) {throw new IllegalArgumentException("Message must have a target.");}if (msg.isInUse()) {throw new IllegalStateException(msg + " This message is already in use.");}synchronized (this) { 如果处于正在退出状态对外抛出一个异常,拒绝消息进入队列并把消息回收掉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;Message p = mMessages;boolean needWake; 将新消息放在链表头部的条件:1,队列为空 ;2,接收到的新消息需要立即处理:when = 0;3,新消息等待处理的时间比链表队头要短这时候只需要将新消息的next指向当前链表的头部,让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 {// 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(); 遍历处理新消息的位置:prev指向p的上一个消息,p开始向队尾移动,如果新消息需要执行的等待时间小于p所指向的消息,就将新消息放在prev和p之间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.nextprev.next = msg;}// We can assume mPtr != 0 because mQuitting is false.如果需要唤醒队列,调用nativeWake(mPtr);唤醒队列if (needWake) {nativeWake(mPtr);}}return true;}
enqueueMessage();是MessageQueue的核心方法,主要功能是向MessageQueue中插入消息
4,next()
Message next() {// Return here if the message loop has already quit and been disposed.// This can happen if the application tries to restart a looper after quit// which is not supported.final long ptr = mPtr; mPtr == 0说明消息队列被释放了if (ptr == 0) {return null;}int pendingIdleHandlerCount = -1; // -1 only during first iterationint nextPollTimeoutMillis = 0;for (;;) {if (nextPollTimeoutMillis != 0) {Binder.flushPendingCommands();}阻塞操作,等待nextPollTimeoutMillis时长nativePollOnce(ptr, nextPollTimeoutMillis);synchronized (this) {// Try to retrieve the next message. Return if found.final long now = SystemClock.uptimeMillis();Message prevMsg = null;Message msg = mMessages;查询队列中的异步消息if (msg != null && msg.target == null) {// Stalled by a barrier. Find the next asynchronous message in the queue.do {prevMsg = msg;msg = msg.next;} while (msg != null && !msg.isAsynchronous());}if (msg != null) {if (now < msg.when) {// Next message is not ready. Set a timeout to wake up when it is ready. 设置下一次查询消息需要等待的时长nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);} else {// Got a message.mBlocked = false;if (prevMsg != null) {prevMsg.next = msg.next;} else {mMessages = msg.next;}msg.next = null;if (DEBUG) Log.v(TAG, "Returning message: " + msg);msg.markInUse();//返回需要执行的消息return msg;}} else {// No more messages.nextPollTimeoutMillis = -1;}如果消息队列正在处于退出状态返回null,调用dispose();释放该消息队列// Process the quit message now that all pending messages have been handled.if (mQuitting) {dispose();return null;}// If first time idle, then get the number of idlers to run.// Idle handles only run if the queue is empty or if the first message// in the queue (possibly a barrier) is due to be handled in the future.if (pendingIdleHandlerCount < 0&& (mMessages == null || now < mMessages.when)) {pendingIdleHandlerCount = mIdleHandlers.size();}if (pendingIdleHandlerCount <= 0) {// No idle handlers to run. Loop and wait some more.mBlocked = true;continue;}if (mPendingIdleHandlers == null) {mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];}mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);}// Run the idle handlers.// We only ever reach this code block during the first iteration.for (int i = 0; i < pendingIdleHandlerCount; i++) {final IdleHandler idler = mPendingIdleHandlers[i];mPendingIdleHandlers[i] = null; // release the reference to the handlerboolean keep = false;try {keep = idler.queueIdle();} catch (Throwable t) {Log.wtf(TAG, "IdleHandler threw exception", t);}if (!keep) {synchronized (this) {mIdleHandlers.remove(idler);}}}// Reset the idle handler count to 0 so we do not run them again.pendingIdleHandlerCount = 0;// While calling an idle handler, a new message could have been delivered// so go back and look again for a pending message without waiting.nextPollTimeoutMillis = 0;}}
next();方法是MessageQueue的和新方法也是比较难理解的一个方法。主要做的工作是从消息队列中取出消息。
void removeMessages(Handler h, int what, Object object) {if (h == null) {return;}synchronized (this) {Message p = mMessages;// Remove all messages at front.while (p != null && p.target == h && p.what == what&& (object == null || p.obj == object)) {Message n = p.next;mMessages = n;p.recycleUnchecked();p = n;}// Remove all messages after front.while (p != null) {Message n = p.next;if (n != null) {if (n.target == h && n.what == what&& (object == null || n.obj == object)) {Message nn = n.next;n.recycleUnchecked();p.next = nn;continue;}}p = n;}}}
移除消息队列中的消息removeMessage();这个方法有两个重载方法,大致逻辑是一样的。在这个方法中有两个while循环,对这两个while讲解比较详细的博客。
上面说过MessageQueue是一个链表,链表分两种:带头节点的不带头节点的。这两种链表的遍历方式不同:不带头节点的链表中,第一个元素需要单独处理,然后将后续部分当作是带头节点的链表使用while循环遍历。MessageQueue是不带头节点的链表,所以我们可以看到有两个while循环。
第一个while循环:如果队列中的第一个Message的target,what,object与指定的handler,what,object相同,删除第一个元素。后续部分就成了带头节点的链表。第二个循环删除链表中后续中符合条件的Message
以上就是对MessageQueue的全部分析内容,有不足的地方或是有误的地方欢迎大家提出指正。
Android 消息机制之深入学习MessageQueue相关推荐
- Android消息机制(Handler、MessageQueue、Looper)详细介绍
Android的消息机制其实在android的开发过程中指的也就是Handler的运行机制,这也就引出了android中常见的面试问题: 简述Handler.Looper.MessageQueue的含 ...
- android自定义队列,Android 消息机制(一)消息队列的创建与循环的开始 Looper与MessageQueue...
写在前面 本文基于Android 7.1.1 (API 25)的源码分析编写 与之前的触摸事件分发机制分析的文章一样,Android系统机制的分析中关键的一环就是事件消息的处理.之前也说过,Andro ...
- Android 系统(177)---Android消息机制分析:Handler、Looper、MessageQueue源码分析
Android消息机制分析:Handler.Looper.MessageQueue源码分析 1.前言 关于Handler消息机制的博客实际上是非常多的了. 之前也是看别人的博客过来的,但是过了一段时间 ...
- Android 进阶14:源码解读 Android 消息机制( Message MessageQueue Handler Looper)
不要心急,一点一点的进步才是最靠谱的. 读完本文你将了解: 前言 Message 如何获取一个消息 Message.obtain() 消息的回收利用 MessageQueue MessageQueue ...
- Android消息机制Handler用法
这篇文章介绍了Android消息机制Handler用法总结,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧 1.简述 Handler消息机制主要包括: Messa ...
- 理解 Android 消息机制
本人只是Android小菜一个,写技术文章只是为了总结自己最近学习到的知识,从来不敢为人师,如果里面有不正确的地方请大家尽情指出,谢谢! 本文基于原生 Android 9.0 源码来解析 Androi ...
- 【腾讯Bugly干货分享】经典随机Crash之二:Android消息机制
为什么80%的码农都做不了架构师?>>> 本文作者:鲁可--腾讯SNG专项测试组 测试工程师 背景 承上经典随机Crash之一:线程安全 问题的模型 好几次灰度top1.top ...
- android消息池,回转寿司你一定吃过!——Android消息机制(构造)
消息机制的故事寿司陈放在寿司碟上,寿司碟按先后顺序被排成队列送上传送带.传送带被启动后,寿司挨个呈现到你面前,你有三种享用寿司的方法. 将Android概念带入后,就变成了Android消息机制的故事 ...
- android handler的机制和原理_一文搞懂handler:彻底明白Android消息机制的原理及源码
提起Android消息机制,想必都不陌生.其中包含三个部分:Handler,MessageQueue以及Looper,三者共同协作,完成消息机制的运行.本篇文章将由浅入深解析Android消息机制的运 ...
- Android 消息机制详解(Android P)
前言 Android 消息机制,一直都是 Android 应用框架层非常重要的一部分,想更加优雅的进行 Android 开发,我想了解消息机制是非常必要的一个过程,此前也分析过很多次 Handler ...
最新文章
- setsockopt 设置socket 详细用法
- 局部变量、全局变量、堆、堆栈、静态和全局
- SAP 电商云 Spartacus UI Angular Component 动态创建的单步调试
- 分享一个点赞超过100的漂亮ASP.NET MVC蓝色界面框架
- language support图标在哪里?怎么消失了?
- 机器学习经典算法笔记——梯度下降算法
- 聪明女人的七个致富之道,学会它你也能成为人人羡慕的小富婆
- linux系统组成以及运行模式
- c语言圆周率天书解释,c语言天书圆周率的计算及分析.doc
- 虚拟蜜罐-honeyd安装部署
- Bootstrap免费字体和图标网站
- 【案例实践】Python多元线性回归、机器学习、深度学习在近红外光谱分析中的实践应用
- 动物miRNA靶基因本地预测(三)—— 靶基因预测与结果分析
- 小程序 报错 errcode: 40029, errmsg: invalid code, hints: [ req_id: HQd79a0747th31 ]
- MySQL全量同步和增量同步-
- Android 绘制渐变色
- 问卷调查小程序功能清单
- 小米业绩再创季度新高,股价或再次上涨,米粉或实现赚一倍的愿望
- Express jade
- matlab主要功能,matlab主要功能