Handler的机制总结
如何保证线程间Looper的唯一性
- Android中通过
Looper.prepare()
生成一个Looper
对象 - 将Looper对象保存在
ThreadLocal
中 - 从
ThreadLocal
获取,来保证每个线程间只会有一个Looper对象 - 当有
Message
要进入Looper
时,从ThreadLocal
中获取Looper
Loop的实现原理
Looper通过Linux的epoll
来实现阻塞唤醒的.
-
epoll
是通过在内核中创建一个虚拟文件来接收外部数据的 - 会与用户进程
mmap
映射同一个物理内存区域 - 当该文件有数据写入时,则内核会将该
FD
的消息通过共享内存区域传递给用户进程
#include
// 创建关联的文件,返回文件描述符
int epoll_create(int size);
// 根据传入的fd来订阅关心的Event
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
// 同步开始等待事件,当事件发生后,返回正在等待的文件描述符的数量
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
Handler.postDelay的实现
Handler是如何实现定时唤醒的,其实也就是通过epoll
中的timeout
来进行阻塞唤醒的.
当有消息要加入MessageQueue
的时候, 会将计算好的时间when
添加到消息链表中
boolean enqueueMessage(Message msg, long when) {
...
synchronized(this){
// 由于直接给对象加锁,所以不需要再申请Lock或者添加volatile等修饰词
...
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// 其中mMessage代表当前正在执行的消息体, 如果enque的消息时间比头指针还要早
// 那么就会唤醒执行任务, 不直接执行任务的原因是这个函数可能会在子线程被调用
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// 如果时间超过的话, 那么就会开始插入消息链表中
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;
}
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
而在MessageQueue.next
函数中,会根据当前时间以及下条消息触发的时间来计算下一条消息timeout
的时间,然后
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) {
if (now < msg.when) {
// 如果被唤醒时,当前时间比下条消息的时间要小, 则认为还没到处理时间
// 则继续计算下一条消息与现在的时间间隔, 重新进入epoll
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// 如果当前时间大于下条时间了,则会返回该条消息分发给对应的
// Handler进行处理
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
// 同时将mMessage设置为下一条要处理的消息
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// 如果链表中没有消息的话,那么就一直等待epoll被唤醒
nextPollTimeoutMillis = -1;
}
// 判断Looper退出
if (mQuitting) {
dispose();
return null;
}
...
// 如果链表中没有消息的话, 就认为的Looper处于IDLE状态, 开始处理空闲状态的回调, 请参照下一条
...
}
MessageQueue.IdleHandler的实现
接上文, 当链表中没有消息的时候, MessageQueue.next()
会走到以下逻辑开始处理Idlehandler
// 当消息链表中没有需要处理的消息时
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)];
}
// 收集当前需要回调的Idlehandler
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// 开始执行IdleHandler的回调
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 {
// 如果idler返回值为false的话,那么就会移除掉该Handler
// 即如果在queueIdle中返回false,则只会响应一次
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
// 从回调中移除该idler
mIdleHandlers.remove(idler);
}
}
}
SyncBarrier的作用与实现
作用
Handler中存在SyncBarrier
, SyncBarrier
的主要作用是阻拦当前链表中的消息执行, 插入高优先级任务优先执行 .
例如, 当前主线程中有A->B->C->D->E五个消息, 而当E需要高优执行的时候(其实就是Android中的VSync重绘任务), 其中A为SyncBarrier
, 则当轮到A执行的时候, MessageQueue
会继续遍历消息链表, 然后找到标记为Asynchronous
的消息, 进行执行该消息.
PS: 即使使用PostDelay
发送的Asynchronous
的消息, 也会阻塞等待该消息执行完后, 才会执行其他的消息.
当需要插入任务时, 使用Handler.getLooper().getMessageQueue().postSyncBarrier()
插入栅栏, 在要插入的消息中设置Message.setAsynchronous(false)
, 代表该消息不是同步执行的即可. 而目前应用层是不能调用postSyncbarrier
函数的, 它被hide
了
实现
postSyncBarrier
其实只是向MessageQueue
中插入了一条消息作为Barrier,而这条消息的特点是:
message.target=null
message.arg1 = token
private int postSyncBarrier(long when) {
// 插入一个SyncBarrier
synchronized (this) {
final int token = mNextBarrierToken++;
final Message msg = Message.obtain();
msg.markInUse(); // 标记该消息正在使用
msg.when = when; // 当前时间戳
msg.arg1 = token; // 标识该Barrier的Token值
Message prev = null;
Message p = mMessages;
if (when != 0) {
while (p != null && p.when <= when) {
// 遍历消息链表, 找到比当前时间小的消息, 保存到prev以及p中
prev = p; // 代表PrevNode
p = p.next; // 代表NextNode
}
}
// 其实就是插入一条消息
if (prev != null) { // invariant: p == prev.next
msg.next = p;
prev.next = msg;
} else {
msg.next = p;
mMessages = msg;
}
return token;
}
}
在MessageQueue
中插入了消息之后,就可以开始插入高优消息了, 例如VSYNC的绘制消息, 最终会调用到postCallbackDelayedInternal
中:
private void postCallbackDelayedInternal(int callbackType,
Object action, Object token, long delayMillis) {
// 其中delayMillis为0
synchronized (mLock) {
final long now = SystemClock.uptimeMillis();
final long dueTime = now + delayMillis;
mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
// 如果不需要delay的话,那么就直接走scheduleFrameLocked开始执行Vsync信号
if (dueTime <= now) {
scheduleFrameLocked(now);
} else {
// 如果有延迟的话,则会将该消息设置为Asynchronus并且插入到MessageQueue中
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
msg.arg1 = callbackType;
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, dueTime);
}
}
}
再来看一下MessageQueue.enqueueMessage
中,对于这种Asynchronous
的消息是怎么处理的
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 {
// 如果Handler所在的线程正在阻塞等待唤醒, 当前已经插入了Barrier,
// 并且插入的消息是Asynchronous的消息的话,则需要唤醒对应线程处理
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
// 遍历链表,找到对应时间的消息
if (p == null || when < p.when) {
break;
}
// 判断插入消息的上一条消息是否是Asynchronous,也就是在这条消前还有
// 需要处理的消息并且这条消息是Asynchronous的话,意味着时间还没到,则不需要唤醒
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// 判断是否需要唤醒, 如果需要的话,就开始唤醒
if (needWake) {
nativeWake(mPtr);
}
而在MessageQueue.next
中,会判断当前要处理的message.target
是否为空, 如果为空则认为是插入了SyncBarrier, 就开始遍历寻找链表中Asynchronous
的消息, 找到了就会返回这条消息,同时使用这条消息计算timeoutMillies
来唤醒线程处理.
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());
}