Handler机制(深入篇)
反思
通过上一节 “handler驱动模型",我们已经明白。Android系统,任何事件(小到滑动,大到 界面绘制)等,都是基于Looper 不停的循环来完成的。那么问题来了:
- 当MessageQueue中,没有待处理的Message时,Looper还是一直进行着循环,那是不是很消耗cpu资源呢?同理,主线程也存在MainLooper,为什么UI线程并不会卡死呢?
- MessageQueue 中,存在着系统相关的Message 和 非系统相关的Message。如果仅仅按照处理时间的优先级,去处理消息的话。那么当Message数量达到一定的级别的话,必然会带来UI绘制卡顿,和掉帧。那么Android系统又是通过什么机制来解决这种问题呢?
epoll 模型(Question 1)
epoll 是一种,Linux系统吓得I/O 复用模型。他是一种阻塞式轮询。
简单的理解,
- 就是当内核缓冲区没有数据写入的时候,处理读出数据的线程就会阻塞调,并掉出了系统的调度队列,暂时不会去瓜分CPU宝贵的时间片了。
- 当内核缓冲区,有数据写入的时候,内核同样会发送事件,唤醒 处理读出数据的线程,重新开始工作。
Looper 机制中,底层就是epoll 模型,通过 navive层方法,达到了这种阻塞式 循环。从而解决了我们遇到的第一个问题。
//MessageQueue.java
public final class MessageQueue {
boolean enqueueMessage(Message msg, long when) {
...
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
// 这里唤醒 nativePollOnce 的沉睡
nativeWake(mPtr);
}
}
Message next() {
...
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
//nativePollOnce 这里陷入沉睡, 等待唤醒
nativePollOnce(ptr, nextPollTimeoutMillis);
...
}
}
nativePollOnce :方法用于“等待”, 直到下一条消息可用为止. 如果在此调用期间花费的时间很长, 则您的主线程没有实际工作要做, 而是等待下一个事件处理.无需担心
nativeWake: MessageQueue 利用名为 epoll 的 Linux 系统调用,当有新的可用Message消息加入的时候。会调用 nativeWake 等待,然后, 内核从等待状态中取出 epoll 等待线程, 并且该线程继续处理新消息
同步屏障(Question2)
简单的理解:是用来分割同步消息和异步消息。它的实际载体是一种 没有持有Hander 的Message.
Message 分为同步 Message 和 异步Message。是通过 FLAG_ASYNCHRONOUS 来标识。一般,我们发送的Message都是 同步消息。而异步Message ,是由系统在特定情况下发出。
//Handler.java
public class Handler{
public Handler() {
this(null, false);
}
public Handler(Looper looper) {
this(looper, null, false);
}
//所有的构造函数都会走到这个重载方法
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
//Message添加到MessageQueue
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
}
//Message.java
public final class Message implements Parcelable {
//设置Message是否为异步消息
public void setAsynchronous(boolean async) {
if (async) {
flags |= FLAG_ASYNCHRONOUS;
} else {
flags &= ~FLAG_ASYNCHRONOUS;
}
}
}
- Hander的 构造函数,最后都会走到最后的 Handler(Callback callback, boolean async) 重载方法。因此,Handler 默认同步Handler(mAsynchronous =false)
- 当Message添加到MessageQueue中的时候,根据mAsynchronous 去设置Message 是否为异步消息
//MessageQueue.java
public final class MessageQueue {
//添加Message
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) {
//如果MessageQueue所在线程死亡,添加的message直接回收掉
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;
//1.当加入的Message处理时间,小于表头Message的时间或者,MessageQueue中的Message数量为0 或者马上要处理,将新的Message 加入到表头
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
//如果当前Looper阻塞,则需要唤醒
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.
//当Looper阻塞,表头是一个同步屏障的时候 且,添加进来的消息是异步消息的时候,需要唤醒
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
//如果加入的message 时间小于 p 的时间
//则将message 插入到 p 前面的节点
if (p == null || when < p.when) {
break;
}
//遍历如果,MessageQueue还存在异步消息的时候,不需要唤醒(neeWake=false)
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;
}
}
- 当添加的Message插入到表头的时候,是否唤起looper 继续进行循环,取决于当前Looper是否处于阻塞
- 当Message 插入到 链表中的时候,当且仅当
1.当前looper处于阻塞
2.表头是同步屏障(handler==null)
3.加入的消息是异步消息,且链表中在加入前不存在 异步消息
才需要唤醒Looper进行循环。
同步屏障的发送
//MessageQueue.java
public final class MessageQueue {
public int postSyncBarrier() {
return postSyncBarrier(SystemClock.uptimeMillis());
}
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) {
//循环遍历,根据时间,找到 要插入的位置 :prev->message->p
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
}
//当MessageQueue 中没有Message时,插入表头
if (prev != null) { // invariant: p == prev.next
msg.next = p;
prev.next = msg;
} else {
//当MessageQueue 中不为空的时候,根据时间插入表中
msg.next = p;
mMessages = msg;
}
return token;
}
}
}
postSyncBarrier: 直接通过obtain 获得的Message,因此该Message 并没有持有 Hander
同步消息异步消息的处理
//MessageQueue.java
public final class MessageQueue {
Message next() {
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());
}
//普通我们发的消息为异步消息,只会走到这个方法(target 不会为null,Asynchronous且为 false)
// mMessage 赋给 msg 返回, mMessage 并指向 mMessage 的下一个message ,
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;
}
}
}
如果,MessageQueue中,遇到了 同步屏障,则在取消息的时候。直接会获取屏障后面的异步消息,而不会取同步消息。因此,当界面绘制的时候,发送屏障 +异步消息的时候,永远都能保证这种消息,优先处理。保证了UI线程的流畅度。