Android Handler机制(二)---MessageQueue源码解析
MessageQueue
1.变量
private final boolean mQuitAllowed;//表示MessageQueue是否允许退出
@SuppressWarnings("unused")
private long mPtr; //mPtr是native代码相关的 Message mMessages; //表示消息队列的头Head
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; // The next barrier token.
// Barriers are indicated by messages with a null target whose arg1 field carries the token.
private int mNextBarrierToken;
mQuitAllowed表示MessageQueue是否允许退出,系统创建的UI线程的MessageQueue是不允许的,其他客户端代码创建的都是允许的;
mPtr是native代码相关的,指向C/C++代码中的某些对象(指针),其他一些nativeXXX()相关的函数本文暂不做分析;
mMessages表示消息队列的头Head;
mIdleHandlers是IdldHandler接口的ArrayList, mPendingIdleHandlers是数组版本,在后面的代码中会将ArrayList的内容拷贝到它里面;
mQuitting表示当前队列是否处于正在退出状态;
mBlocked表示next()调用是否被block在timeout不为0的pollOnce上;
mNextBarrierToken表示下一个barrier token,barrier用target==null, arg1==token的Message对象表示;
IdleHandler callback接口:
public static interface IdleHandler {
/**
* Called when the message queue has run out of messages and will now
* wait for more. Return true to keep your idle handler active, false
* to have it removed. This may be called if there are still messages
* pending in the queue, but they are all scheduled to be dispatched
* after the current time.
*/
boolean queueIdle();
}
public void addIdleHandler(IdleHandler handler) {
if (handler == null) {
throw new NullPointerException("Can't add a null IdleHandler");
}
synchronized (this) {
mIdleHandlers.add(handler);
}
}
public void removeIdleHandler(IdleHandler handler) {
synchronized (this) {
mIdleHandlers.remove(handler);
}
IdleHandler接口表示当MessageQueue发现当前没有更多消息可以处理的时候则顺便干点别的事情的callback函数(即如果发现idle了,
那就找点别的事干)。callback函数有个boolean的返回值,表示是否keep。如果返回false,则它会在调用完毕之后从mIdleHandlers
中移除。这里让我们来看一个具体的例子(实现),ActivityThread.java里的一个内部类,代码如下:
final class GcIdler implements MessageQueue.IdleHandler {
@Override
public final boolean queueIdle() {
doGcIfNeeded();
return false;
}
}
这是一个gc相关的IdleHandler,即如果没有更多的消息可以处理就会抽空doGcIfNeeded(),最后返回false表示不保留在mIdleHandlers
中,即用一次就扔了,只执行一遍。
MQ的构造方法:在Looper中就是调用的这个构造方法。
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed; //true是允许推出
mPtr = nativeInit(); //这是一个本地方法
}
MQ的构造方法简单的调用了nativeInit()来进行初始化,这是一个jni方法,也就是说,可能是在JNI层维持了它这个消息队列的对象。在message中有好多native方法,可以看出message是比较底层的一个类。
接下来看MessageQueue的核心方法,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;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
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;
}
// 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 handler
boolean 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;
}
}
首先初始化2个接下来要用到的变量,紧接着进入无限for循环中,其某次循环主要做这么几件事情:
1. 如果nextPollTimeoutMillis != 0的话,调用Binder.flushPendingCommands();
2. 调用nativePollOnce(mPtr, nextPollTimeoutMillis);
3. 进入一个大的同步块,尝试获取一个可以处理的消息,具体做法是,记录当前时间now,初始化变量prevMsg为null,msg为mMessges;
如果msg是一个sync barrier消息,则直奔下一个asynchronous消息(这之间的所有同步消息会被本次循环忽略,也就是说遇到这种情况,
next方法会从找到的异步消息的位置开始尝试获取一个可以处理的消息并返回),同时更新prevMsg,msg的值;
4.当退出此do...while循环的时候msg可能为空(走到队列尾了),或者成功找到了一个这样的(异步)消息。
5.如果是到队尾了即msg==null,则表示没更多的消息了,设置nextPollTimeoutMillis = -1;否则当now<msg.when(msg的时间还没到),设置一个合理的等待时间,即调用
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
6.当msg到了该处理的时间了,也就是说我们找到了这样一个消息可以返回了,设置mBlocked为false,将msg从mMessages队列中取出来(类似单链表的删除操作),并执行
msg.next=null、msg.markInUse(),返回msg。
7.如果到这一步了还没return的话,那说明还没有可以处理的消息,检查下队列是否要求退出了,如果是执行dispose(),返回null。当Looper的loop方法看到null的message的时候会退出loop。
8.接下来既然没消息可以处理,那就该处理IdleHandler了。如果pendingIdleHandlerCount小于0(注意其在第一次进入for循环是被初始化为-1)且没更多的消息需要处理,设置pendingIdleHandlerCount=mIdleHandlers.size();
9.如果pendingIdleHandlerCount还是<=0的话,表示没有idle handler需要执行,
设置mBlocked为true,接着进入下次循环。
10.接下来就是根据mIdleHandlers来初始化mPendingIdleHandlers。退出同步块后我们就剩下最后一件事了,那就是run Idle handlers。一个for循环用来做这就事情,在循环内如果IdleHandler没必要保留,则会从mIdleHandlers中移除。
11. 最后重置pendingIdleHandlerCount为0(也就是4只会在第一次循环的时候执行一次),将nextPollTimeoutMillis设为0,因为当我们在
执行4的时候,新的Message可能已经到来了,所以我们需要立即开始(不需要等待)下次循环来检查。
**上面很长,简单点说就是:next方法取出下一个Message(从头部取),如果没有Message可以处理,就可以处理下IdleHandler。
看完了next()方法,接下来我们来看enqueue()方法:
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.");
}
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;
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();
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.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
在后边介绍Handler的时候,可以看到,许多跟message(包括runnable)相关的操作,最终都delegate给了MessageQueue的enqueue方法。
和任何方法一样,在enqueue之前,也都是对参数msg的检查,比如msg如果在使用中,或者msg的target是null,都会抛出AndroidRuntimeException,进行完条件检查后,会进入真正的处理逻辑。接下来的操作类似在一张单链表中插入一个元素:进入同步块
1. 如果此队列处于正在退出的状态则不能在往里入队了,不能插入元素了,在这种情况下会抛出RuntimeException,然后return false,
表示失败了;
2. 接下来表示队列的状态ok,设置msg的when字段,临时变量p指向队列头;(必要的初始化,准备工作)
3. 如果队列是空的或when==0或when<p.when,也就是说要插入的这个message应该在第一个位置也就是队首,那么它将是新的Head,将它和原先的队列连接起来;
4. 否则插入将发生在队列中间的某个位置(有可能是队尾),将msg插在第一个p的前面,p满足这个条件(p == null || when < p.when)。
最后退出同步块,返回true,表示操作(入队)成功。
**messageQueue中的元素是按序按时间先后插入的(先执行的在前)。
removeMessages():
接着我们来看3个类似的removeMessages()方法,
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;
}
}
}
void removeMessages(Handler h, Runnable r, Object object) {
if (h == null || r == null) {
return;
}
synchronized (this) {
Message p = mMessages;
// Remove all messages at front.
while (p != null && p.target == h && p.callback == r
&& (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.callback == r
&& (object == null || n.obj == object)) {
Message nn = n.next;
n.recycleUnchecked();
p.next = nn;
continue;
}
}
p = n;
}
}
}
void removeCallbacksAndMessages(Handler h, Object object) {
if (h == null) {
return;
}
synchronized (this) {
Message p = mMessages;
// Remove all messages at front.
while (p != null && p.target == h
&& (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 && (object == null || n.obj == object)) {
Message nn = n.next;
n.recycleUnchecked();
p.next = nn;
continue;
}
}
p = n;
}
}
}
这3个方法只是remove的条件不同,其主逻辑都是相同的,即从队列中删除所有匹配的元素。总体思想都是先从队首删除,如果删除了则队首
指向接下来的元素,重复这个过程,直到第一个不匹配的元素出现。接着从这个元素之后(after front)开始查找并删除,
方法是链表删除后一个节点的方法,即p.next=nn。注意这里都是删除所有匹配的消息,而不是第一个匹配的。
最后看下队列的quit()方法:
void quit(boolean safe) {
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
synchronized (this) {
if (mQuitting) {
return;
}
mQuitting = true;
if (safe) {
removeAllFutureMessagesLocked();
} else {
removeAllMessagesLocked();
}
// We can assume mPtr != 0 because mQuitting was previously false.
nativeWake(mPtr);
}
}
quit方法根据所传参数safe的值,有2种不同的退出策略,如果是safe的退出,则执行removeAllFutureMessagesLocked(),
其内部的逻辑为如果队首的元素还没到期那说明队列中其他所有的元素也都没到期,所以等同于删除所有的消息即调用
removeAllMessagesLocked();否则遍历队列找到第一个p.when>now这样的message元素p,(更新队列的尾部p.next=null,
缩短队列)从p开始一直到队列结束都是要被删掉的元素,全部删除之;如果是unsafe的退出,则所有message都直接被删除并
回收即调用removeAllMessagesLocked()。
Message是链表结构,MessageQueue中的好多操作都是基于链表,看起来困难的话,可以画画图,就很清晰啦。。。
Message的源码分析:http://www.cnblogs.com/jycboy/p/5786551.html
MessageQueue源码解析:http://www.cnblogs.com/jycboy/p/5786682.html
Handler源码解析:http://www.cnblogs.com/jycboy/p/5791457.html
Message源码解析:http://www.cnblogs.com/jycboy/p/5786551.html
转发注明出处:http://www.cnblogs.com/jycboy/p/5786682.html
Android Handler机制(二)---MessageQueue源码解析的更多相关文章
- Android Handler机制(三)----Looper源码解析
一.Looper Looper对象,顾名思义,直译过来就是循环的意思,从MessageQueue中不断取出message. Class used to run a message loop for a ...
- Handler机制原理图、源码、使用!!!!!
android的消息处理机制——Looper,Handler,Message (原理图.源码) 转自:http://my.oschina.net/u/1391648/blog/282892 在开始讨 ...
- Android IntentService使用介绍以及源码解析
版权声明:本文出自汪磊的博客,转载请务必注明出处. 一.IntentService概述及使用举例 IntentService内部实现机制用到了HandlerThread,如果对HandlerThrea ...
- 【Android应用开发】EasyDialog 源码解析
示例源码下载 : http://download.csdn.net/detail/han1202012/9115227 EasyDialog 简介 : -- 作用 : 用于在界面进行一些介绍, 说明; ...
- Android HandlerThread使用介绍以及源码解析
摘要: 版权声明:本文出自汪磊的博客,转载请务必注明出处. 一.HandlerThread的介绍及使用举例 HandlerThread是什么鬼?其本质就是一个线程,但是Han ...
- Android 手势识别类 ( 二 ) GestureDetector 源码浅析
前言:Android 关于手势的操作提供两种形式:一种是针对用户手指在屏幕上划出的动作而进行移动的检测,这些手势的检测通过android提供的监听器来实现:另一种是用 户手指在屏幕上滑动而形成一定的不 ...
- Android FM 模块学习之四 源码解析(1)
Normal 0 7.8 磅 0 2 false false false EN-US ZH-CN X-NONE MicrosoftInternetExplorer4 前一章我们了解了FM手动调频,接下 ...
- Android FM模块学习之四源码解析(一)
转自:http://blog.csdn.net/tfslovexizi/article/details/41516149?utm_source=tuicool&utm_medium=refer ...
- 十二.jQuery源码解析之.eq().first().last().slice()
eq(index):将集合中的索引为index的元素提取出来. first():返回集合中的第一个元素. .last():防护集合中的最后一个元素. .slice(start[,end]):返回集合中 ...
随机推荐
- Android SlidingMenu 仿网易新闻客户端布局
前面两篇文章中的SlidingMenu都出现在左侧,今天来模仿一下网易新闻客户端左右两边都有SlidingMenu的效果,以下是网易新闻客户端效果: 不扯闲话了,直接进入正题吧 frame_conte ...
- Android正则表达式使用及性能隐患分析
场景:找出一个关键词在一条短信中出现的次数 使用正则的实现方式: public static int findKeyWordCount(String srcText, String keyword) ...
- java类的初始化和对象的创建顺序
学习java编程思想--类的初始化p146 类的加载顺序* 1加载器启动找到 xxx.class文件,通过extends关键字寻找基类,先加载基类* 2类初始化先初始化static成员变量和stati ...
- Glyphish – 精心打造的 iPhone & iPad 应用程序图标
Glyphish 是一套精心打造的图标库,包含 iOS 工具栏.标签栏.导航条等等,Glyphish 图标也完美的用在 Android.Windows Mobile App 和移动网站的 UI 设计等 ...
- SQL Server 2014里的针对基数估计的新设计(New Design for Cardinality Estimation)
对于SQL Server数据库来说,性能一直是一个绕不开的话题.而当我们去分析和研究性能问题时,执行计划又是一个我们一直关注的重点之一. 我们知道,在进行编译时,SQL Server会根据当前的数据库 ...
- 自绘制HT For Web ComboBox下拉框组件
传统的HTML5的下拉框select只能实现简单的文字下拉列表,而HTforWeb通用组件中ComboBox不仅能够实现传统HTML5下拉框效果,而且可以在文本框和下拉列表中添加自定义的小图标,让整个 ...
- Auto Mapper04(MVC中的配置)
学习如何在MVC项目中配置AutoMapper. 一:首先在MVC项目中引用AutoMapper的DLL文件,接着创建一个接口,这里面我们需要定义两个方法,接口里面的方法只能定义不能实现,也没有什么修 ...
- ES6模块的import和export用法总结
ES6之前以前出现了js模块加载的方案,最主要的是CommonJS和AMD规范.commonjs前者主要应用于服务器,实现同步加载,如nodejs.AMD规范应用于浏览器,如requirejs,为异步 ...
- C#如何调用COM
这章中描述的属性被用在创建和COM程序交互的程序中. 1.1 COMImport 属性 当被放在一个类上, COMImport 属性就把这个类标记为一个外部实现的COM 类.这样的一个类声明使得可以 ...
- 自己写的一个简单的jQuery提示插件
代码: /** * 2014年11月13日 * 提示插件 */ (function ($) { $.fn.tips = function (text) { var divtipsstyle = &qu ...