源码分析->消息屏障是什么鬼?

源码分析基于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实现并定时发送;

以上分析有不对的地方,请指出,互相学习,谢谢哦!

你可能感兴趣的:(源码分析->消息屏障是什么鬼?)