之前说过一次Handler,主要介绍了sendMessage 到 handleMessage的流程,具体的细节并没有过多涉及,本文会补充Handler的两个细节:
- 阻塞唤醒机制
- 内存屏障
Looper
Looper的创建:
Looper构造方法:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
Looper只有一个私有构造方法,在构造方法中,会创建一个MessageQueue对象;
主线程Looper对象的创建:
public static void prepareMainLooper() {
// 分析1
prepare(false);
synchronized (Looper.class) {
// 分析2
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
// 分析3
sMainLooper = myLooper();
}
}
// 分析1
private static void prepare(boolean quitAllowed) {
// 分析2
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
// 分析3
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
在ActivityThread的main中调用prepareMainLooper()
为当前App的主线程构造一个Looper对象;
分析1: 调用prepare() new一个Looper
分析: 保证一个线程只能创建一个Looper对象
分析3:通过ThreadLocal做到Looper的线程隔离
子线程Looper的创建就是调用prepare(),注意这里的quitAllowed
参数,表示Looper是否可以quit(),子线程可以,主线程不可以;
Loop的循环:loop()
public static void loop() {
// 获取当前Looper的MessageQueue对象
final MessageQueue queue = me.mQueue;
// 死循环,不断的从MessageQueue中取消息
for (;;) {
// 分析1
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
try {
// 分析2
msg.target.dispatchMessage(msg);
}
}
}
在ActivityThread的main中构造了主线的Looper对象后,会调用Looper的loop(),在loop()首先获取当前Looper绑定的MessageQueue对象,然后在一个死循环里不断的取Message,分发给对应的Handler处理
- 分析1: 调用MessageQueue的next()获取一个Message对象,这个方法是一个阻塞方法,后面会详细讲;
- 分析2:分发给对应的handler处理;上篇文章有详细介绍;
Looper循环的退出:quit()
public void quit() {
mQueue.quit(false);
}
Looper的退出调用了MessageQueue的退出,具体内容在MessageQueue部分展开;
MessageQueue
在具体介绍MessageQueue之前,我们先看一下MessageQueue是如何保存Message的:
public final class MessageQueue {
Message mMessages;
}
public final class Message implements Parcelable {
/*package*/ Message next;
}
我们可以看到MessageQueue的内部只有一个Message成员,并没有其他的数据结构存放Message; 而Message本身是一个链表的结点;
MessageQueue获取Message :next()
Message next() {
// 分析1
int nextPollTimeoutMillis = 0;
for (;;) {
// 分析2
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
if (msg != null) {
// 分析3
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 {
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
return msg;
}
} else {
// No more messages. // 分析4
nextPollTimeoutMillis = -1;
}
// 分析5
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
}
- 分析1:
nextPollTimeoutMillis
字段,用于判断是否阻塞 - 分析2:nativePollOnce()这是一个native方法,用于实现next()的阻塞,根据
nextPollTimeoutMillis
的大小,-1表示一直阻塞等到有人唤醒,0表示不阻塞,大于0表示阻塞多少时间后自动唤醒; - 分析3:MessageQueue是一个优先级队列,当取出的Message还没有没有到执行时间时,会计算出一个时间差,通过nativePollOnce()取阻塞一定时间;
- 分析4:如果没有取到Message,将nextPollTimeoutMillis字段置为-1,一直阻塞等待唤醒;
- 分析5:检查
mQuitting
字段,上文的Looper退出循环调用quit会将此字段设置为true,回收当前的MessageQueue;
MessageQueue添加Message:enqueueMessage()
boolean enqueueMessage(Message msg, long when) {
synchronized (this) {
// 分析1
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;
// 分析:2
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;
}
// 分析3
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
MessageQueue是一个优先级队列,在使用Handler提交Message的时候,会附加一个时间,这个时间会通过when字段保存,在添加到队列的时候,会通过插入排序的方式找到当前Message在队列的合适位置;
- 分析1: Message的优先级
when字段
- 分析2:插入排序
- 分析3:唤醒操作,当next()中的nativePollOnce()传入-1时,会一直阻塞,在这里会唤醒这个阻塞;
Message
Message的复用:obtain()
private static Message sPool; // 缓存链表
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();
}
在Message类的内部会有一个Pool链表,用来复用Message对象,调用obtain()创建Message时会优先从Pool中取,如果娶不到才会去new;
Message Pool的添加:
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++;
}
}
}
public void recycle() {
if (isInUse()) {
if (gCheckRecycle) {
throw new IllegalStateException("This message cannot be recycled because it "
+ "is still in use.");
}
return;
}
recycleUnchecked();
}
在recycleUnchecked()
中,先将Message对象的所有成员清空,如果Pool没有超过容量的话就添加到pool中;外部使用recycle()复用Message,这个方法的调用时机很多,ActivityThread的H,Wifi服务都用了这个回收
内存屏障:
在安卓中,整个App的运行都是在Loop.loop()死循环中完成了,包括我们的项目逻辑,安卓的系统逻辑,比如屏幕的刷新等,安卓系统规定了屏幕一般16ms刷新一次,刷新操作是由ViewRootImpl来实现的,触发屏幕刷新的机制也是一个Message消息,但是我们刷新屏幕的Message必须是立刻执行的,由于MessageQueue是优先级队列的原理,无法做到实时的刷新,就提出了Handler内存屏障
的概念;
Message节点被分成两种:同步和异步
message.setAsynchronous(true); // 设置为异步Message
Message有一个字段,用于表示异步还是同步,默认是同步,可以通过setAsynchronous()
设置,在一些特殊的场景下,比如View的刷新,必须立刻执行,就需要设置异步Message;
内存屏障的打开与关闭:
单纯的设置同步和异步是无法保证立刻执行的,还必须打开内存屏障:内存屏障的本质是一个target==null的Message
,当loop的时候发现有内存屏障的时候,此时只会处理异步Message,保证了异步Message的立刻执行;
MessageQueue.postSyncBarrier()
private int postSyncBarrier(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.markInUse();
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;
}
}
通过postSyncBarrier
方法,就可以添加一个target==null的Message,表示打开了内存屏障;
MessageQueue.removeSyncBarrier()
public 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.recycleUnchecked();
// If the loop is quitting then it is already awake.
// We can assume mPtr != 0 when mQuitting is false.
if (needWake && !mQuitting) {
nativeWake(mPtr);
}
}
}
通过removeSyncBarrier
删除内存屏障节点,关闭内存屏障;
ViewRootImpl.scheduleTraversals()
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
void unscheduleTraversals() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
mChoreographer.removeCallbacks(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
}
}
在ViewRootImpl内中,就是使用这两个方法做到的立刻刷新
内存屏障的目的和使用介绍了,我们看看MessageQueue是如何实现异步消息的立刻执行的:
MessageQueue.next()
Message next() {
...
for (;;) {
synchronized (this) {
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());
}
...
}
}
其他多余的代码就不贴了,上面已经贴过了,当发现一个target== null的Message的时候,会用一个do while 去取异步的Message;