Android消息机制(二):Message和MessageQu
Message
消息结构
每个消息用Message表示,Message主要包含以下内容:
filed | 含义 | 说明 |
---|---|---|
what | 消息类别 | 由用户定义,用来区分不同的消息 |
arg1 | 参数1 | 是一种轻量级的传递数据的方式 |
arg2 | 参数2 | 是一种轻量级的传递数据的方式 |
obj | 消息内容 | 任意对象,但是使用Messenger跨进程传递Message时不能为null |
data | Bundle数据 | 比较复杂的数据建议使用该变量(相比上面几个,这个县的比较重量级) |
target | 消息响应方 | 关联的Handler对象,处理Message时会调用它分发处理Message对象 |
when | 触发响应时间 | 处理消息时间 |
next |
Message队列里的下一个Message对象 | 用next指向下一条Message,实现一个链表数据结构,用户一般使用不到该字段。 |
这里的用户指一般的APP开发者。
一般不用手动设置target,调用Handler.obtainMessage()方法会自动的设置Message的target为当前的Handler。
得到Message之后可以调用sendToTarget(),发送消息给Handler,Handler再把消息放到message queue的尾部。
对Message除了给部分成员变量赋值外的操作都可以交由Handler来处理。
消息池
在通过Handler发送消息时,我们可以通过代码Message message=new Message();
新建一条消息,但是我们并不推荐这样做,因为这样每次都会新建一条消息,很容易造成资源浪费。Android中设计了消息池用于避免该现象:
- 获取消息
obtain()
从消息池中获取消息:Message msg=Message.obtain();
obtain()方法源码:
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null; //从sPool中取出一个Message对象,并消息链表断开
m.flags = 0; // clear in-use flag清除in-use flag
sPoolSize--;//消息池的可用大小进行-1操作
return m;
}
}
return new Message();// 当消息池为空时,直接创建Message对象
}
从消息池取Message,都是把消息池表头的Message取走,再把表头指向下一条消息
next
;
- 回收消息
recycle()
把不再使用的消息回收到消息池mgs.recycle();
recycle()方法源码:
public void recycle() {
if (isInUse()) {//判断消息是否正在使用
if (gCheckRecycle) {
throw new IllegalStateException("This message cannot be recycled because it "
+ "is still in use.");
}
return;
}
recycleUnchecked();
}
/**
* 对于不再使用的消息,加入到消息池
* Recycles a Message that may be in-use.
* Used internally by the MessageQueue and Looper when disposing of queued Messages.
*/
void recycleUnchecked() {
// Mark the message as in use while it remains in the recycled object pool.
// Clear out all other details.
//将消息标示位置为IN_USE,并清空消息所有的参数。
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = -1;
when = 0;
target = null;
callback = null;
data = null;
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {//当消息池没有满时,将Message对象加入消息池
next = sPool;
sPool = this;
sPoolSize++;//消息池的可用大小进行加1操作
}
}
}
消息回收,就是将Message内容重置后,再把Message加到链表的表头,加入到消息池的过程;
MessageQueue
负责管理消息队列,实际上Message类有一个next字段,会将Message对象串在一起成为一个消息队列,所以并不需要LinkedList之类的数据结构将Message对象组在一起成为队列。
- 创建消息队列
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();//通过native方法初始化消息队列,其中mPtr是供native代码使用
}
在MessageQueue初始化的时候调用了nativeInit,这是一个Native方法:
static void android_os_MessageQueue_nativeInit(JNIEnv* env, jobject obj) {
NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
if (!nativeMessageQueue) {
jniThrowRuntimeException(env, "Unable to allocate native queue");
return;
}
nativeMessageQueue->incStrong(env);
android_os_MessageQueue_setNativeMessageQueue(env, obj, nativeMessageQueue);
}
static void android_os_MessageQueue_setNativeMessageQueue(JNIEnv* env, jobject messageQueueObj,
NativeMessageQueue* nativeMessageQueue) {
env->SetIntField(messageQueueObj, gMessageQueueClassInfo.mPtr,
reinterpret_cast<jint>(nativeMessageQueue));
}
方法名由java层类的包名+类名+方法名组成,这不是标准,是习惯写法,也可以采用其他名称组合,具体是什么名称由JNINativeMethod方法中Java对象与c++对象的映射决定,此处是JNI方面的内容,不作过多解释。
在nativeInit中,new了一个Native层的MessageQueue的对象,并将其地址保存在了Java层MessageQueue的成员mPtr中,Android中有好多这样的实现,一个类在Java层与Native层都有实现,通过JNI的GetFieldID与SetIntField把Native层的类的实例地址保存到Java层类的实例的mPtr成员中,比如Parcel。
再看NativeMessageQueue的实现:
NativeMessageQueue::NativeMessageQueue() : mInCallback(false), mExceptionObj(NULL) {
mLooper = Looper::getForThread();
if (mLooper == NULL) {
mLooper = new Looper(false);
Looper::setForThread(mLooper);
}
}
-
消息入队
enqueueMessage()
enqueueMessage 用于将Message对象插入消息队列。MessageQueue永远是按照Message触发的时间先后顺序排列的,队头的消息是将要最早触发的消息。当有消息需要加入消息队列时,会从队列头开始遍历,直到找到消息应该插入的合适位置,以保证所有消息的时间顺序。
该方法会被Handler对象调用。
源码如下:
/**
* 添加一条消息到消息队列
* @param msg 要添加的消息
* @param when 消息处理时间
* @return 添加成功与否
*/
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {// 每一个Message必须有一个target
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.
//p为null(代表MessageQueue没有消息) 或者msg的触发时间是队列中最早的, 则进入该该分支
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
//将消息按时间顺序插入到MessageQueue。一般地,不需要唤醒事件队列,除非
//消息队头存在barrier,并且同时Message是队列中最早的异步消息。
// 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.
//消息没有退出,我们认为此时mPtr != 0
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
-
消息轮询
next()
最重要的方法,用于获取下一个Message对象,如果没有需要处理的Message对象,该方法将阻塞。MessageQueue用本地方法做同步互斥,因为这样时间更精准。每个Message对象都有一个什么时刻处理该Message对象的属性when,没到时间都不会处理该Message对象,如果时间不精准的话,会导致系统消息不能及时处理。
/**
* 依次从MessageQueue中取出Message
* @return 消息
*/
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 循环迭代的首次为-1
int 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) {
//查询MessageQueue中的下一条异步消息
// 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();//设置消息flag成使用状态
return msg;//成功地获取MessageQueue中的下一条即将要执行的消息
}
} else {
// No more messages.//没有消息了
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {//消息正在退出,返回null
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) {
//没有idle handlers 需要运行,则循环并等待。
// 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.
//只有第一次循环时,会运行idle handlers,执行完成后,重置pendingIdleHandlerCount为0.
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler//去掉handler的引用
boolean keep = false;
try {
keep = idler.queueIdle();//idle时执行的方法
} 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.
//重置idle handler个数为0,以保证不会再次重复运行
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.
//当调用一个空闲handler时,一个新message能够被分发,因此无需等待可以直接查询pending message.
nextPollTimeoutMillis = 0;
}
}
nativePollOnce(ptr, nextPollTimeoutMillis)是一个native方法,是一个阻塞操作。其中nextPollTimeoutMillis代表下一个消息到来前,还需要等待的时长;当nextPollTimeoutMillis = -1时,表示消息队列中无消息,会一直等待下去。空闲后,往往会执行IdleHandler中的方法。当nativePollOnce()返回后,next()从mMessages中提取一个消息。nativePollOnce()在native做了大量的工作,想深入研究可查看资料: Android消息机制2-Handler(Native层)。
-
移除消息
removeMessages()
就是将消息从链表移除,同时将移除的消息添加到消息池,提供循环复用。
采用了两个while循环,第一个循环是从队头开始,移除符合条件的消息,第二个循环是从头部移除完连续的满足条件的消息之后,再从队列后面继续查询是否有满足条件的消息需要被移除。
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;
}
}
}
-
退出消息队列
消息退出的方式:- 当safe =true时,只移除尚未触发的所有消息,对于正在触发的消息并不移除;
- 当safe =flase时,移除所有的消息
void quit(boolean safe) {
if (!mQuitAllowed) {// 当mQuitAllowed为false,表示不运行退出,强行调用quit()会抛出异常
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.
//mQuitting=false,那么认定为 mPtr != 0
nativeWake(mPtr);
}
}