源码分析基于Android-28
什么是消息屏障?
当要处理的消息为消息屏障,则延迟执行后续的普通消息,优先执行异步消息,常用在绘制任务的处理。消息屏障使得消息处理有了优先级。
Tips:有些博文说它是同步屏障,其实两者是同一个东西。
处理流程
如图所示,Msg1、Msg2、Msg3、Msg4均为普通消息,Msg5为异步消息。当在Msg1、Msg2之后插入一个消息屏障,那么Msg3、Msg4将会被延迟执行,Msg5优先执行。当消息屏障被移除,普通消息会得到执行机会。
处理流程.png
绘制任务处理流程
1.发送消息屏障
setContentView最终会调到以下方法,
ViewRootImpl.scheduleTraversals
主要工作:
(1)postSyncBarrier,往主线程队列发送一个SyncBarrier消息,也就是消息屏障,告诉主线程队列后续有绘制任务,普通消息延迟执行,消息屏幕是没有target的,这有别于普通消息;
Tips:插入消息屏障是不会唤醒线程的;
(2)mChoreographer.postCallback,注册绘制任务以及申请VSync信号;
Tips:VSync信号是SurfaceFlinger实现并定时发送;VSync信号监听器在是实例化mChoreographer成员变量mDisplayEventReceiver 的时候注册的;
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
......
}
}
Choreographer.postCallback最终调到postCallbackDelayedInternal
主要作用:
(1)mCallbackQueues[callbackType].addCallbackLocked,按时间先后顺序,将任务放入单链表中;
Tips:mCallbackQueues是一个长度为4的数组,每个元素都是一个单链表,保存跟输入、动画、绘制相关的任务;
(2)scheduleFrameLocked,申请下一次VSync信号。
private void postCallbackDelayedInternal(int callbackType,
Object action, Object token, long delayMillis) {
......
synchronized (mLock) {
final long now = SystemClock.uptimeMillis();
final long dueTime = now + delayMillis;
mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
scheduleFrameLocked(now);
......
}
2.接收VSync信号
Choreographer.FrameDisplayEventReceiver.onVsync
主要工作:
(1)当VSync屏幕刷新信号到来会触发该方法,方法发送了一个异步消息到主线程队列,任务就是自己本身,这个任务就是需要优先执行的。
(2)doFrame,回调到上述postCallback mTraversalRunnable的run方法,最终会执行ViewRootImpl.doTraversal;
@Override
public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {
......
Message msg = Message.obtain(mHandler, this);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
}
@Override
public void run() {
mHavePendingVsync = false;
doFrame(mTimestampNanos, mFrame);
}
Choreographer.doFrame
主要工作:
有个很重要的Log信息,jitterNanos为该帧延迟执行的时间,时间定于16毫秒且延迟30帧,则打印出Log.i(TAG, "Skipped " + skippedFrames + " frames! "
+ "The application may be doing too much work on its main thread.");
void doFrame(long frameTimeNanos, int frame) {
.......
long intendedFrameTimeNanos = frameTimeNanos;//预期执行时间
startNanos = System.nanoTime();//当前时间
final long jitterNanos = startNanos - frameTimeNanos;
if (jitterNanos >= mFrameIntervalNanos) {
final long skippedFrames = jitterNanos / mFrameIntervalNanos;
if (skippedFrames >= SKIPPED_FRAME_WARNING_LIMIT) {
Log.i(TAG, "Skipped " + skippedFrames + " frames! "
+ "The application may be doing too much work on its main thread.");
}
......
}
3.处理消息屏障
MessageQueue.next
主要工作:
(1)for循环,不断地取消息出来处理;
(2)当消息不为空且消息target为空,则是消息屏障,则从后续消息中取出异步消息来进行处理;
(3)如果异步消息还没有到时间,则休眠等待;
(3)如果异步消息到时间,则把消息返回;
Message next() {
......
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());
}
if (msg != null) {
if (now < msg.when) {
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;
}
......
}
上回消息屏障会在绘制任务执行的ViewRootImpl.doTraversal方法中移除,这样就能普通消息正常执行。
补充:Choreographer
该类中文名字编舞者,它负责通过内部类的父类DisplayEventReceiver.scheduleVsync注册VSync信号,通过内部类FrameDisplayEventReceiver.onVsync接收信号。
总结
(1)ViewRootImpl发消息屏障到主线程消息队列,Choreographer把绘制任务放入队列,并申请VSync信号;
(2)Choreographer的内部类FrameDisplayEventReceiver提供接收VSync信号以及申请VSync信号的方法;
(3)当FrameDisplayEventReceiver接收到VSync信号,往主线程发送异步消息,消息任务就是自己本身;
(4)MessageQueue取到异步消息进行处理;
(5)FrameDisplayEventReceiver.run方法被执行,遍历绘制任务队列,以此执行;
(6)最后移除消息屏障;
(7)VSync信号是SurfaceFlinger实现并定时发送;
以上分析有不对的地方,请指出,互相学习,谢谢哦!
网友评论