Message
Message是一个消息类,信息携带者。
// 可以表明是哪个message,类似于id
public int what;
// 两个int属性值,主要是保存一些简单点的值
public int arg1;
public int arg2;
// 发送给接收器的任意对象。当使用Message对象在线程间传递消息时,如果它包含一个Parcelable的结构类(不是由应用程序实现的类),此字段必须为非空(non-null)。
public Object obj;
// 指明此message发送到何处的可选Messenger对象。具体的使用方法由发送者和接受者决定
public Messenger replyTo;
/*package*/ static final int FLAG_IN_USE = 1 << 0; // 判断Message是否在使用
/*package*/ static final int FLAG_ASYNCHRONOUS = 1 << 1; // 如果设置message是异步的
/*package*/ static final int FLAGS_TO_CLEAR_ON_COPY_FROM = FLAG_IN_USE; // 明确在copyFrom方法
// 标志Message的作用或者状态
/*package*/ int flags;
/*package*/ long when;
// arg1和arg2只能储存int整形类型,Bundle可以存很多其他的信息,如String
/*package*/ Bundle data;
// 处理Message的目标Handler
/*package*/ Handler target;
// 当我们用handler.post时,callback就是post的runnable
/*package*/ Runnable callback;
// 这是重点1,从这个属性可以看出,Message作为节点相连,形成Message链表
/*package*/ Message next;
private static final Object sPoolSync = new Object();
// 这是重点2,从这个属性可以看出,Message处理完后,会加入到sPool池中
private static Message sPool;
private static int sPoolSize = 0;
private static final int MAX_POOL_SIZE = 50;
从上面属性,要理解3个知识点:
- Message是消息的携带者
- Message有下个节点(Message)的引用,可以组成是一个单向链表的结构,
- Message有一个池缓存起来,当然,只保留链头
So,看到这里,我们就不要再写new Message()这类不理智的语句了。那我们怎么获取一个Message?obtain()方法,代码如下:
public static Message obtain() {
synchronized (sPoolSync) {
// 如果链头不为null
if (sPool != null) {
Message m = sPool;
// sPool往后挪一个
sPool = m.next;
// 断开m和sPool的关系,m就独立了
m.next = null;
// 减一
sPoolSize--;
// 返回
return m;
}
}
return new Message();
}
因为有缓存嘛,所以我们Message处理完之后,是要被回收的。
怎么被回收呢,代码如下:
public void recycle() {
// 因为要被重用嘛,所以要清理所有携带的信息
clearForRecycle();
// 加入缓存中,成为sPool的链头,注意,不能超过MAX_POOL_SIZE
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
/*package*/ void clearForRecycle() {
flags = 0;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
when = 0;
target = null;
callback = null;
data = null;
}
至于关于Message异步的标志等其他,有兴趣的看源码。
MessageQueue
MessageQueue名为消息队列,其实不是一个队列,保存的是Message,是一个单向链表结构。
因为MessageQueue是一个偏底层的类,还有native本地方法,所以这里只关注Message怎么加入和取出队列。
加入队列代码:
boolean enqueueMessage(Message msg, long when) {
if (msg.isInUse()) {
throw new AndroidRuntimeException(msg + " This message is already in use.");
}
// 如果msg没有target的话,报错
if (msg.target == null) {
throw new AndroidRuntimeException("Message must have a target.");
}
// 同步
synchronized (this) {
// 如果队列已经退出,报错
if (mQuitting) {
RuntimeException e = new RuntimeException(
msg.target + " sending message to a Handler on a dead thread");
Log.w("MessageQueue", e.getMessage(), e);
return false;
}
msg.when = when;
Message p = mMessages;
boolean needWake;
// 比较message的when时间属性按从小到大插入到队列中
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) {
// 通过native层唤醒队列
// 生产者-消费者模式,生产者有产品的时候一般情况下会唤醒消费者
nativeWake(mPtr);
}
}
return true;
}
Message加入队列还是比较容易理解的,再看看取出Message的代码:
// protected是我自己加的,源码没有的,发现没有protected,注释没看清楚
protected Message next() {
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
// We can assume mPtr != 0 because the loop is obviously still running.
// The looper will not call this method after the loop quits.
// 通过native层阻塞nextPollTimeoutMillis时间
nativePollOnce(mPtr, 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.
// 如果设置了barrier消息屏障,就取下一条异步消息,如果有的话(我们一般使用的消息是同步的,除非Message设置成异步)
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.
// 获取到了Message
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
// msg移出链表
msg.next = null;
if (false) Log.v("MessageQueue", "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;
}
// 空闲的时候处理idle
// 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("MessageQueue", "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;
}
}
上面提到消息屏障的东西,我查了一下好的理解方法,叫“同步分割栏”。可以被理解为一个特殊Message,它的target域为null。它不能通过sendMessageAtTime()等函数打入到消息队列里,而只能通过调用Looper的postSyncBarrier()来打入。
// Looper代码
public int postSyncBarrier() {
// 加入"同步分割栏"
return mQueue.enqueueSyncBarrier(SystemClock.uptimeMillis());
}
public void removeSyncBarrier(int token) {
// 移除"同步分割栏"
mQueue.removeSyncBarrier(token);
}
// MessageQueue代码
int enqueueSyncBarrier(long when) {
// Enqueue a new sync barrier token.
// We don't need to wake the queue because the purpose of a barrier is to stall it.
synchronized (this) {
final int token = mNextBarrierToken++;
final Message msg = Message.obtain();
msg.when = when;
msg.arg1 = token;
Message prev = null;
Message p = mMessages;
if (when != 0) {
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
}
if (prev != null) { // invariant: p == prev.next
msg.next = p;
prev.next = msg;
} else {
msg.next = p;
mMessages = msg;
}
return token;
}
}
void removeSyncBarrier(int token) {
// Remove a sync barrier token from the queue.
// If the queue is no longer stalled by a barrier then wake it.
synchronized (this) {
Message prev = null;
Message p = mMessages;
while (p != null && (p.target != null || p.arg1 != token)) {
prev = p;
p = p.next;
}
if (p == null) {
throw new IllegalStateException("The specified message queue synchronization "
+ " barrier token has not been posted or has already been removed.");
}
final boolean needWake;
if (prev != null) {
prev.next = p.next;
needWake = false;
} else {
mMessages = p.next;
needWake = mMessages == null || mMessages.target != null;
}
p.recycle();
// If the loop is quitting then it is already awake.
// We can assume mPtr != 0 when mQuitting is false.
if (needWake && !mQuitting) {
nativeWake(mPtr);
}
}
}
“同步分割栏”是起什么作用的呢?它就像一个卡子,卡在消息链表中的某个位置,当消息循环不断从消息链表中摘取消息并进行处理时,一旦遇到这种“同步分割栏”,那么即使在分割栏之后还有若干已经到时的同步Message,也不会摘取这些消息了。请注意,此时只是不会摘取“同步Message”了,如果队列中还有“异步Message”,那么还是会摘取已到时的“异步Message”的。注意,如果不移除“同步分割栏”,同步信息会一直被卡住,不会被摘取。
另一个要说的是那些Idle Handler,当消息队列中没有消息需要马上处理时,会判断用户是否设置了Idle Handler,如果有的话,则会尝试处理mIdleHandlers中所记录的所有Idle Handler,此时会逐个调用这些Idle Handler的queueIdle()成员函数。我们举一个例子,在ActivityThread中,在某种情况下会在消息队列中设置GcIdler,进行垃圾收集,其定义如下:
final class GcIdler implements MessageQueue.IdleHandler {
@Override
public final boolean queueIdle() {
doGcIfNeeded();
return false;
}
}