2、MessageQueue
2.0概述
MessgaeQueue就是一个队列,用来存储消息,MessageQueue属于某一个Looper。Java层的MessageQueue包含一个mPtr变量,保存了Native层的NativeMessageQueue的地址。MessageQueue对外提供了几个方法:
1、新建队列:主要是在native层的NativeInit方法实现。
2、元素入队:enqueueMessage
3、元素出队:Message next()
4、删除元素:removeMessage
5、销毁队列:主要是native层的nativeDestory方法。
2.1 MessageQueue的创建
MessageQueue类定义在 frameworks/base/core/java/android/os/MessageQueue.java
中
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
在MessageQueue构造方法中,首先传入的quitAllowed
表示是否允许退出。然后定义了一个mPtr变量,用来存储Native层MessageQueue地址,这个后面会用到。调用nativeInit()
方法.位于frameworks/base/core/jni/android_os_MessageQueue.cpp
2.1.1nativeInit()
static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
if (!nativeMessageQueue) {
jniThrowRuntimeException(env, "Unable to allocate native queue");
return 0;
}
nativeMessageQueue->incStrong(env);
return reinterpret_cast<jlong>(nativeMessageQueue);
}
在NativeInit中创建了一个NativeMessageQueue对象,并增加其引用计数(因为这个类是继承自RefBase,也就是c++智能指针)。然后调用了reinterpret_cast<jlong>
方法,将nativeMessageQueue的地址转化为long类型,return回去给Java层,存储在mPtr中,这样Java层的MessageQueue就和NativeMessageQueue关联起来了。
2.1.2 NativeMessageQueue的构造方法
此部分代码为获取或者创建了Looper对象,详情请查看Looper部分 Handler Looper MessageQueue之Looper
2.2 enqueueMessage
handler的sendMessage
方法最终会调用当前handler
的成员变量MessageQueue
的enqueueMessage
方法(java层)
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;
}
msg.target
中存储的就是该方法最终处理的handler,也就是在这里将handler和message关联起来的,msg.isInUse()
该消息是否已经被使用了,新消息默认是不会被使用的。
同步块中的mQuitting
标志是用来表示处理消息的messagequeue
所在线程是否已经异常退出。此标志在void quit(boolean safe)
方法中设置,也就是用户调用了quit方法,才会退出。mMessage
是MQ中的消息队列,其中有一个next
指针指向下一个Message
,Message
在队列中是按when
排列的,mMessage
是消息头部,也就是当前要处理的消息,若消息队列为空(p==null
)或者新消息需要立即处理(when == 0
)或者新消息时间最小(when<p.when
),此时会把此消息加入到消息队列的头部。其他情况,则是遍历消息队列,找到一个合适的位置(时间大于或队列末尾),放置此消息。
mBlocked
表示消息处理线程是否block,默认为false,若头部加了新消息,且当前是阻塞状态,则会唤醒该线程开始处理消息。另外,若果是异步消息(p.isAsynchronous == true
)则,不需要唤醒线程。唤醒线程是调用的nativeWake
方法,在c端实现,位置如下:frameworks/base/core/jni/android_os_MessageQueue.cpp
中。mPtr
存储了JNI层NativeMessageQueue地址。这里通过reinterpret_cast方法把该地址转换成nativeMessageQueue对象,最终调用其wake方法,如下:
static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jlong ptr) {
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
nativeMessageQueue->wake();
}
void NativeMessageQueue::wake() {
mLooper->wake();
}
故最终调用了JNI层Looper的wake()
,此部分见Looper部分 Handler Looper MessageQueue之Looper
3.3 MessageQueue.next
next方法位于frameworks/base/core/java/android/os/MessageQueue.java
中,作用是循环监听获取消息,并且可能造成阻塞,或者idelHandler空闲处理。
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;
}
}
该方法首先调用了nativePollOnce.这个方法位于jni层frameworks/base/core/jni/android_os_MessageQueue.cpp
,参数obj也就是Java层传过来的mPtr,保存的是JNI层NativeMessageQueue*,所以最终调用的是nativeMessageQueue的pollOnce方法。nativePollOnce后会根据消息队列的状态判断是否应该进入空闲处理状态,有以下几种情况会进入空闲处理:
1.消息处理时间未到( now < msg.when
)
2.消息为空( mMessages == null
)
还有一种情况是,满足执行空闲处理,但是空闲处理函数数组为空(pendingIdleHandlerCount = mIdleHandlers.size()
),此时会把mBlocked
设置为true
,使得线程阻塞。
后续会分析这个idelHandler部分内容。
static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
jlong ptr, jint timeoutMillis) {
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
nativeMessageQueue->pollOnce(env, obj, timeoutMillis);
}
void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) {
mPollEnv = env;
mPollObj = pollObj;
mLooper->pollOnce(timeoutMillis);
mPollObj = NULL;
mPollEnv = NULL;
if (mExceptionObj) {
env->Throw(mExceptionObj);
env->DeleteLocalRef(mExceptionObj);
mExceptionObj = NULL;
}
}
最终调用到Looper层的pollOnce方法,此部分见Looper部分 Handler Looper MessageQueue之Looper
2.4MessageQueue之IdleHandler
线程处理机制中,有一种特殊情况:当线程处于空闲状态(线程消息队列为空或者线程消息队列头的处理时间未到)的时候如何处理消息。此时会执行空闲处理函数,由IdleHandler指定。
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(@NonNull IdleHandler handler) {
if (handler == null) {
throw new NullPointerException("Can't add a null IdleHandler");
}
synchronized (this) {
mIdleHandlers.add(handler);
}
}
public void removeIdleHandler(@NonNull IdleHandler handler) {
synchronized (this) {
mIdleHandlers.remove(handler);
}
}
空闲消息处理部分用于线程在暂无消息处理时做一些辅助工作,例如ActivityThread中的IdleHandler就是在GIdler中完成内存垃圾回收,另一个用途是在Home启动后,进入空闲状态时,发送BOOT_COMPLETED广播。
2.5Message之recyle
消息处理后,需要回收重新放到消息池中,这部分用到了recyle方法,回收过程是将消息清空,此部分方法位于frameworks/base/core/java/android/os/Message.java
中
public void recycle() {
if (isInUse()) {
if (gCheckRecycle) {
throw new IllegalStateException("This message cannot be recycled because it "
+ "is still in use.");
}
return;
}
recycleUnchecked();
}
void recycleUnchecked() {
// Mark the message as in use while it remains in the recycled object pool.
// Clear out all other details.
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) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
其中,sPoll指向消息池首部,回收消息的时候,把新消息加到头部,然后消息池可用消息数加1。所以,在创建消息的时候推荐使用Message.obtain方法,从消息池中取一个消息,达到复用,而不是新建对象
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}